Desktop-suite.ahk für Windows 10 IoT 2021 LTSC: Unterschied zwischen den Versionen
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
Admin (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
| Zeile 1: | Zeile 1: | ||
== Anleitung: Desktop-Suite == | |||
=== Was ist das? === | |||
Ein AutoHotkey-v2-Skript, das drei Dinge in einem bündelt: eine '''Desktop-Leiste''' in der Taskleiste zum Anzeigen und Wechseln der virtuellen Windows-Desktops, ein '''Autotile''', das Fenster automatisch in Spalten oder einem Quad-Layout anordnet, und einen '''Dark/Light-Umschalter''' für das Windows-Farbschema. Dazu kommt ein Always-on-Top-Schalter für einzelne Fenster. | |||
<pre> | === Voraussetzungen === | ||
Du brauchst '''AutoHotkey v2''' (installiert) und die '''VirtualDesktopAccessor.dll''' unter <code>C:\Tools\VirtualDesktopAccessor\VirtualDesktopAccessor.dll</code>. Liegt die DLL woanders, passe die Zeile <code>DllPath := ...</code> ganz oben im Skript an. Ohne die DLL startet das Skript nicht (es meldet sich mit einer Fehlermeldung). | |||
=== Starten === | |||
Doppelklick auf <code>desktop-suite.ahk</code>. Es erscheint das grüne AHK-Symbol im Infobereich und links neben der Uhr die Desktop-Leiste mit den nummerierten Knöpfen. Soll das Skript bei jedem Windows-Start mitlaufen: <code>Win+R</code> → <code>shell:startup</code> → Enter, und eine Verknüpfung der Datei in diesen Ordner legen. | |||
Nach jeder Änderung am Skript: Rechtsklick aufs AHK-Symbol im Infobereich → '''Reload Script'''. | |||
=== Virtuelle Desktops === | |||
Die Leiste zeigt alle Desktops als Knöpfe; der aktive leuchtet blau. Klick auf einen Knopf wechselt dorthin. Legst du in Windows Desktops an oder löscht welche (Win+Tab), passt sich die Leiste automatisch an. Bei Vollbild-Anwendungen (Videos, Spiele) blendet sie sich selbst aus. | |||
{| class="wikitable" | |||
!Kürzel | |||
!Wirkung | |||
|- | |||
|'''Win + 1…9''' | |||
|zu Desktop 1–9 wechseln | |||
|- | |||
|'''Win + Shift + 1…9''' | |||
|aktives Fenster dorthin schicken, du bleibst hier | |||
|- | |||
|'''Win + Alt + 1…9''' | |||
|Fenster verschieben und mitspringen | |||
|- | |||
|'''Win + 0''' | |||
|Desktop-Leiste ein-/ausblenden | |||
|} | |||
=== Autotile === | |||
Das Autotile ordnet alle „normalen" Fenster des Bildschirms automatisch an — minimierte Fenster, Dialoge und Fenster auf anderen Desktops bleiben unberührt. Solange es an ist, fügen sich neu geöffnete Fenster von selbst ein und geschlossene machen Platz. | |||
{| class="wikitable" | |||
!Kürzel | |||
!Wirkung | |||
|- | |||
|'''Strg + Shift + Win + Z''' | |||
|Autotile an, Layout '''Spalten''' (alle nebeneinander) | |||
|- | |||
|'''Strg + Shift + Win + J''' | |||
|Autotile an, Layout '''Quad''' (links eine große Hälfte, Rest rechts gestapelt — bei 3 Fenstern: halb + zwei Viertel) | |||
|- | |||
|'''Strg + Shift + Win + Y''' | |||
|Autotile aus | |||
|- | |||
|'''Strg + Shift + Win + N''' | |||
|aktives Fenster eine Position weiterschieben | |||
|- | |||
|'''Strg + Shift + Win + B''' | |||
|aktives Fenster eine Position zurückschieben | |||
|} | |||
Tipp: Im Quad-Layout bestimmt die Position 1 den großen Platz links — mit N/B schiebst du das gewünschte Fenster dorthin. Die Abstände lassen sich oben im Skript über <code>GAP_INNER</code> und <code>GAP_OUTER</code> ändern. | |||
=== Dark/Light umschalten === | |||
{| class="wikitable" | |||
!Kürzel | |||
!Wirkung | |||
|- | |||
|'''Strg + Shift + Win + O''' | |||
|Windows-Farbschema zwischen Dunkel und Hell umschalten | |||
|} | |||
Schaltet App- und System-Theme gleichzeitig um; Taskleiste, Explorer und die meisten Apps wechseln sofort. Ein kurzes Tooltip zeigt den neuen Zustand. Einzelne Programme mit eigener Theme-Einstellung (z. B. Browser auf „festes Theme") folgen ihrer eigenen Einstellung — stelle sie dort auf „System", wenn sie mitwechseln sollen. | |||
=== Always on Top === | |||
{| class="wikitable" | |||
!Kürzel | |||
!Wirkung | |||
|- | |||
|'''Strg + Alt + T''' | |||
|aktives Fenster immer im Vordergrund halten (an/aus) | |||
|- | |||
|'''Shift + Rechtsklick''' auf eine Titelleiste | |||
|dasselbe für das angeklickte Fenster | |||
|} | |||
=== Wenn etwas klemmt === | |||
Reagieren die Kürzel nicht, prüfe, ob das Skript läuft (grünes Symbol im Infobereich) und ob du wirklich alle drei Modifier hältst (erst Strg+Shift+Win gedrückt halten, dann den Buchstaben). Meldet das Skript beim Start die fehlende DLL, stimmt der Pfad in <code>DllPath</code> nicht. Und wenn sich Fenster beim Kacheln überlappen, sind es meist zu viele für Spalten — dann aufs Quad wechseln (J) oder Fenster auf mehrere virtuelle Desktops verteilen.<pre> | |||
#Requires AutoHotkey v2.0 | #Requires AutoHotkey v2.0 | ||
Version vom 16. Juli 2026, 21:50 Uhr
Anleitung: Desktop-Suite
Was ist das?
Ein AutoHotkey-v2-Skript, das drei Dinge in einem bündelt: eine Desktop-Leiste in der Taskleiste zum Anzeigen und Wechseln der virtuellen Windows-Desktops, ein Autotile, das Fenster automatisch in Spalten oder einem Quad-Layout anordnet, und einen Dark/Light-Umschalter für das Windows-Farbschema. Dazu kommt ein Always-on-Top-Schalter für einzelne Fenster.
Voraussetzungen
Du brauchst AutoHotkey v2 (installiert) und die VirtualDesktopAccessor.dll unter C:\Tools\VirtualDesktopAccessor\VirtualDesktopAccessor.dll. Liegt die DLL woanders, passe die Zeile DllPath := ... ganz oben im Skript an. Ohne die DLL startet das Skript nicht (es meldet sich mit einer Fehlermeldung).
Starten
Doppelklick auf desktop-suite.ahk. Es erscheint das grüne AHK-Symbol im Infobereich und links neben der Uhr die Desktop-Leiste mit den nummerierten Knöpfen. Soll das Skript bei jedem Windows-Start mitlaufen: Win+R → shell:startup → Enter, und eine Verknüpfung der Datei in diesen Ordner legen.
Nach jeder Änderung am Skript: Rechtsklick aufs AHK-Symbol im Infobereich → Reload Script.
Virtuelle Desktops
Die Leiste zeigt alle Desktops als Knöpfe; der aktive leuchtet blau. Klick auf einen Knopf wechselt dorthin. Legst du in Windows Desktops an oder löscht welche (Win+Tab), passt sich die Leiste automatisch an. Bei Vollbild-Anwendungen (Videos, Spiele) blendet sie sich selbst aus.
| Kürzel | Wirkung |
|---|---|
| Win + 1…9 | zu Desktop 1–9 wechseln |
| Win + Shift + 1…9 | aktives Fenster dorthin schicken, du bleibst hier |
| Win + Alt + 1…9 | Fenster verschieben und mitspringen |
| Win + 0 | Desktop-Leiste ein-/ausblenden |
Autotile
Das Autotile ordnet alle „normalen" Fenster des Bildschirms automatisch an — minimierte Fenster, Dialoge und Fenster auf anderen Desktops bleiben unberührt. Solange es an ist, fügen sich neu geöffnete Fenster von selbst ein und geschlossene machen Platz.
| Kürzel | Wirkung |
|---|---|
| Strg + Shift + Win + Z | Autotile an, Layout Spalten (alle nebeneinander) |
| Strg + Shift + Win + J | Autotile an, Layout Quad (links eine große Hälfte, Rest rechts gestapelt — bei 3 Fenstern: halb + zwei Viertel) |
| Strg + Shift + Win + Y | Autotile aus |
| Strg + Shift + Win + N | aktives Fenster eine Position weiterschieben |
| Strg + Shift + Win + B | aktives Fenster eine Position zurückschieben |
Tipp: Im Quad-Layout bestimmt die Position 1 den großen Platz links — mit N/B schiebst du das gewünschte Fenster dorthin. Die Abstände lassen sich oben im Skript über GAP_INNER und GAP_OUTER ändern.
Dark/Light umschalten
| Kürzel | Wirkung |
|---|---|
| Strg + Shift + Win + O | Windows-Farbschema zwischen Dunkel und Hell umschalten |
Schaltet App- und System-Theme gleichzeitig um; Taskleiste, Explorer und die meisten Apps wechseln sofort. Ein kurzes Tooltip zeigt den neuen Zustand. Einzelne Programme mit eigener Theme-Einstellung (z. B. Browser auf „festes Theme") folgen ihrer eigenen Einstellung — stelle sie dort auf „System", wenn sie mitwechseln sollen.
Always on Top
| Kürzel | Wirkung |
|---|---|
| Strg + Alt + T | aktives Fenster immer im Vordergrund halten (an/aus) |
| Shift + Rechtsklick auf eine Titelleiste | dasselbe für das angeklickte Fenster |
Wenn etwas klemmt
Reagieren die Kürzel nicht, prüfe, ob das Skript läuft (grünes Symbol im Infobereich) und ob du wirklich alle drei Modifier hältst (erst Strg+Shift+Win gedrückt halten, dann den Buchstaben). Meldet das Skript beim Start die fehlende DLL, stimmt der Pfad in DllPath nicht. Und wenn sich Fenster beim Kacheln überlappen, sind es meist zu viele für Spalten — dann aufs Quad wechseln (J) oder Fenster auf mehrere virtuelle Desktops verteilen.
#Requires AutoHotkey v2.0
#SingleInstance Force
CoordMode "Mouse", "Screen"
; ╔══════════════════════════════════════════════════════════════╗
; ║ Desktop-Suite: ║
; ║ 1) Virtuelle-Desktop-Leiste in der Taskleiste (vdesk) ║
; ║ 2) Autotile — Spalten / Quad ║
; ║ 3) Dark/Light-Umschalter ║
; ╚══════════════════════════════════════════════════════════════╝
;
; ── Virtuelle Desktops ──────────────────────────────────────
; Win+1..9 Desktop wechseln
; Win+Shift+1..9 aktives Fenster dorthin schicken (du bleibst)
; Win+Alt+1..9 Fenster verschieben UND mitspringen
; Win+0 Desktop-Leiste ein/aus
;
; ── Autotile (Strg+Shift+Win + Buchstabe) ───────────────────
; Strg+Shift+Win+Z Autotile AN, Layout Spalten
; Strg+Shift+Win+J Autotile AN, Layout Quad (links halb, Rest rechts)
; Strg+Shift+Win+Y Autotile AUS
; Strg+Shift+Win+N aktives Fenster eine Position weiter
; Strg+Shift+Win+B aktives Fenster eine Position zurueck
;
; ── Sonstiges ───────────────────────────────────────────────
; Strg+Shift+Win+O Windows Dark/Light umschalten
; Strg+Alt+T Always on Top fuer aktives Fenster
; Shift+Rechtsklick auf Titelleiste -> Always on Top
; ══════════════════════════════════════════════════════════════
; ══ TEIL 1: VIRTUELLE-DESKTOP-LEISTE ══
; ══════════════════════════════════════════════════════════════
; ══ Einstellungen (Leiste) ═══════════════════════════════════
DllPath := "C:\Tools\VirtualDesktopAccessor.dll"
BTN_W := 28 ; Breite pro Button
BTN_H := 26 ; Hoehe
GAP := 2 ; Abstand zwischen Buttons
PAD := 14 ; Abstand zum Infobereich (Uhr/Icons)
COL_BG := 0x1F1F1F ; Panel-Hintergrund (Taskleistenton)
COL_ON := 0x0A84FF ; aktiver Desktop
COL_OFF := 0x3A3A3A ; inaktive Desktops
AUTOHIDE := true ; Panel bei Vollbild automatisch ausblenden
; ═════════════════════════════════════════════════════════════
hDll := DllCall("LoadLibrary", "Str", DllPath, "Ptr")
if !hDll {
MsgBox "VirtualDesktopAccessor.dll nicht gefunden:`n" DllPath
ExitApp
}
pGoTo := DllCall("GetProcAddress", "Ptr", hDll, "AStr", "GoToDesktopNumber", "Ptr")
pMove := DllCall("GetProcAddress", "Ptr", hDll, "AStr", "MoveWindowToDesktopNumber", "Ptr")
pCurrent := DllCall("GetProcAddress", "Ptr", hDll, "AStr", "GetCurrentDesktopNumber", "Ptr")
pCount := DllCall("GetProcAddress", "Ptr", hDll, "AStr", "GetDesktopCount", "Ptr")
pPin := DllCall("GetProcAddress", "Ptr", hDll, "AStr", "PinWindow", "Ptr")
DeskCount() => DllCall(pCount, "Int")
DeskCurrent() => DllCall(pCurrent, "Int") + 1
; ══ Kernfunktionen ═══════════════════════════════════════════
VDGo(n) {
global pGoTo
if (n > DeskCount()) {
Flash("Desktop " n " existiert nicht")
return
}
DllCall(pGoTo, "Int", n - 1)
}
MoveWin(n, follow := false) {
global pMove, pGoTo, LastWin
if (n > DeskCount()) {
Flash("Desktop " n " existiert nicht")
return
}
hwnd := WinExist("A")
if (hwnd = PanelHwnd())
hwnd := LastWin
if !hwnd
return
DllCall(pMove, "Ptr", hwnd, "Int", n - 1)
if follow
DllCall(pGoTo, "Int", n - 1)
else
Flash("-> Desktop " n)
}
; kleines Overlay (gemeinsam fuer alle Teile)
Flash(txt) {
ToolTip txt
SetTimer () => ToolTip(), -900
}
; ══ Panel ════════════════════════════════════════════════════
global Panel := "", Tabs := [], LastCount := 0, LastCur := 0
global LastWin := 0, LastPos := "", Visible := true, Manual := true
PanelHwnd() => Panel ? Panel.Hwnd : 0
PanelWidth(cnt) => cnt * BTN_W + (cnt - 1) * GAP + 2 * GAP
TabClick(n, *) {
VDGo(n)
}
BuildPanel(cnt) {
global Panel, Tabs, BTN_W, BTN_H, GAP, COL_BG, COL_OFF, pPin
global LastCur, LastPos, Visible
if Panel
Panel.Destroy()
Tabs := []
Panel := Gui("-Caption +ToolWindow +AlwaysOnTop +E0x08000000 -DPIScale")
Panel.BackColor := COL_BG
Panel.MarginX := 0, Panel.MarginY := 0
Loop cnt {
Panel.SetFont("s10 Bold cFFFFFF", "Segoe UI")
c := Panel.Add("Text", Format("x{} y{} w{} h{} Center Background{:06X}"
, GAP + (A_Index-1)*(BTN_W+GAP), GAP
, BTN_W, BTN_H - 2*GAP, COL_OFF), A_Index)
c.OnEvent("Click", TabClick.Bind(A_Index))
Tabs.Push(c)
}
Panel.Show(Format("NoActivate Hide w{} h{}", PanelWidth(cnt), BTN_H))
DllCall(pPin, "Ptr", Panel.Hwnd) ; auf allen Desktops sichtbar
LastPos := ""
Place(cnt)
Visible := false
LastCur := 0
Refresh(true)
}
Place(cnt) {
global Panel, BTN_H, PAD, LastPos
hTray := WinExist("ahk_class Shell_TrayWnd")
if !hTray
return
WinGetPos &tx, &ty, &tw, &th, "ahk_id " hTray
w := PanelWidth(cnt)
x := tx + tw - 300 - w ; Fallback
hNotify := DllCall("FindWindowEx", "Ptr", hTray, "Ptr", 0
, "Str", "TrayNotifyWnd", "Ptr", 0, "Ptr")
if hNotify {
r := Buffer(16, 0)
DllCall("GetWindowRect", "Ptr", hNotify, "Ptr", r)
x := NumGet(r, 0, "Int") - w - PAD ; links vom Infobereich
}
y := ty + (th - BTN_H) // 2
pos := x "," y
if (pos = LastPos)
return
LastPos := pos
; SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE
DllCall("SetWindowPos", "Ptr", Panel.Hwnd, "Ptr", 0
, "Int", x, "Int", y, "Int", 0, "Int", 0, "UInt", 0x0015)
}
Raise() {
global Panel
; SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE, HWND_TOPMOST
DllCall("SetWindowPos", "Ptr", Panel.Hwnd, "Ptr", -1
, "Int", 0, "Int", 0, "Int", 0, "Int", 0, "UInt", 0x0013)
}
; ── Vollbild-Erkennung ───────────────────────────────────────
; Wie die Shell: aktives Fenster deckt den ganzen Monitor ab
; und ist weder Desktop noch Taskleiste.
IsFullscreen() {
hwnd := WinExist("A")
if !hwnd || (hwnd = PanelHwnd())
return false
cls := WinGetClass(hwnd)
if (cls = "WorkerW" || cls = "Progman" || cls = "Shell_TrayWnd")
return false
r := Buffer(16, 0)
if !DllCall("GetWindowRect", "Ptr", hwnd, "Ptr", r)
return false
wx := NumGet(r, 0, "Int"), wy := NumGet(r, 4, "Int")
wr := NumGet(r, 8, "Int"), wb := NumGet(r, 12, "Int")
; Monitor unter diesem Fenster (MONITOR_DEFAULTTONEAREST)
hMon := DllCall("MonitorFromWindow", "Ptr", hwnd, "UInt", 2, "Ptr")
mi := Buffer(40, 0)
NumPut("UInt", 40, mi, 0)
if !DllCall("GetMonitorInfo", "Ptr", hMon, "Ptr", mi)
return false
mx := NumGet(mi, 4, "Int"), my := NumGet(mi, 8, "Int")
mr := NumGet(mi, 12, "Int"), mb := NumGet(mi, 16, "Int")
return (wx <= mx) && (wy <= my) && (wr >= mr) && (wb >= mb)
}
SetVisible(show) {
global Panel, Visible
if (show = Visible)
return
Visible := show
if show {
Panel.Show("NoActivate")
Raise()
} else
Panel.Hide()
}
Paint(idx, active) {
global Tabs, COL_ON, COL_OFF
c := Tabs[idx]
c.SetFont(active ? "s10 Bold cFFFFFF" : "s10 Norm cA0A0A0", "Segoe UI")
c.Opt("+Background" Format("{:06X}", active ? COL_ON : COL_OFF))
c.Redraw()
}
Refresh(force := false) {
global Tabs, LastCur, LastCount, LastWin, Panel
global AUTOHIDE, Manual, Visible
if (h := WinExist("A")) && (h != PanelHwnd())
LastWin := h
cnt := DeskCount()
if (cnt != LastCount) {
LastCount := cnt
BuildPanel(cnt)
return
}
; sichtbar = manuell an UND (kein Autohide ODER kein Vollbild)
SetVisible(Manual && (!AUTOHIDE || !IsFullscreen()))
if !Visible
return
Place(cnt)
Raise()
cur := DeskCurrent()
if (cur = LastCur && !force)
return
LastCur := cur
Loop Tabs.Length
Paint(A_Index, A_Index = cur)
; RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_UPDATENOW
DllCall("RedrawWindow", "Ptr", Panel.Hwnd, "Ptr", 0, "Ptr", 0, "UInt", 0x0185)
}
LastCount := DeskCount()
BuildPanel(LastCount)
SetTimer Refresh, 250
; ══════════════════════════════════════════════════════════════
; ══ TEIL 2: AUTOTILE (Spalten / Quad) ══
; ══════════════════════════════════════════════════════════════
; ══ Einstellungen (Autotile) ═════════════════════════════════
global GAP_INNER := 8 ; Abstand zwischen Fenstern (px)
global GAP_OUTER := 8 ; Abstand zum Bildschirmrand (px)
global Layout := "columns" ; Startlayout: "columns" oder "grid"
; ═════════════════════════════════════════════════════════════
global AutoTile := false, LastSig := "", Tiling := false
global TileOrder := []
; ══ Fenster-Filter: was darf gekachelt werden? ═══════════════
IsManageable(hwnd) {
try {
if !hwnd
return false
if !DllCall("IsWindowVisible", "Ptr", hwnd)
return false
; auf anderem virtuellen Desktop / versteckt (UWP) -> ueberspringen
cloaked := 0
DllCall("dwmapi\DwmGetWindowAttribute", "Ptr", hwnd
, "UInt", 14, "Int*", &cloaked, "UInt", 4) ; DWMWA_CLOAKED
if cloaked
return false
if (WinGetTitle(hwnd) = "")
return false
if (WinGetMinMax(hwnd) = -1) ; minimiert
return false
style := WinGetStyle(hwnd)
ex := WinGetExStyle(hwnd)
if (ex & 0x80) ; WS_EX_TOOLWINDOW
return false
; Fenster mit Owner (Dialoge/Popups) raus, ausser echte AppWindows
owner := DllCall("GetWindow", "Ptr", hwnd, "UInt", 4, "Ptr") ; GW_OWNER
if (owner && !(ex & 0x40000)) ; WS_EX_APPWINDOW
return false
; nur groessenveraenderbare Fenster kacheln (fixe Dialoge floaten lassen)
if !(style & 0x40000) ; WS_THICKFRAME
return false
static skip := Map("Progman",1, "WorkerW",1, "Shell_TrayWnd",1
, "Shell_SecondaryTrayWnd",1, "Windows.UI.Core.CoreWindow",1)
if skip.Has(WinGetClass(hwnd))
return false
return true
}
return false
}
MonOf(hwnd) => DllCall("MonitorFromWindow", "Ptr", hwnd, "UInt", 2, "Ptr") ; NEAREST
; Arbeitsbereich eines Monitors (ohne Taskleiste)
WorkArea(hMon) {
mi := Buffer(40, 0), NumPut("UInt", 40, mi, 0)
DllCall("GetMonitorInfo", "Ptr", hMon, "Ptr", mi)
return {l: NumGet(mi,20,"Int"), t: NumGet(mi,24,"Int")
, r: NumGet(mi,28,"Int"), b: NumGet(mi,32,"Int")}
}
; DWM-Rahmenversatz: echter Fensterrahmen vs. sichtbare Kante
FrameOffsets(hwnd) {
ow := Buffer(16,0), fw := Buffer(16,0)
DllCall("GetWindowRect", "Ptr", hwnd, "Ptr", ow)
if DllCall("dwmapi\DwmGetWindowAttribute", "Ptr", hwnd
, "UInt", 9, "Ptr", fw, "UInt", 16) ; EXTENDED_FRAME_BOUNDS
return {l:0, t:0, r:0, b:0}
return { l: NumGet(fw,0,"Int") - NumGet(ow,0,"Int")
, t: NumGet(fw,4,"Int") - NumGet(ow,4,"Int")
, r: NumGet(ow,8,"Int") - NumGet(fw,8,"Int")
, b: NumGet(ow,12,"Int") - NumGet(fw,12,"Int") }
}
; Fenster an sichtbarer Zielposition platzieren (Versatz rausgerechnet)
PlaceWindow(hwnd, x, y, w, h) {
try {
if (WinGetMinMax(hwnd) = 1) ; maximiert -> erst normal
WinRestore(hwnd)
o := FrameOffsets(hwnd)
DllCall("SetWindowPos", "Ptr", hwnd, "Ptr", 0
, "Int", x - o.l, "Int", y - o.t
, "Int", w + o.l + o.r, "Int", h + o.t + o.b
, "UInt", 0x14) ; NOZORDER | NOACTIVATE
}
}
; ══ Layouts ══════════════════════════════════════════════════
LayColumns(n, a) {
global GAP_INNER, GAP_OUTER
rects := []
L := a.l+GAP_OUTER, T := a.t+GAP_OUTER, R := a.r-GAP_OUTER, B := a.b-GAP_OUTER
colW := (R - L - (n-1)*GAP_INNER) / n
Loop n {
cx := L + (A_Index-1)*(colW+GAP_INNER)
rects.Push({x:Round(cx), y:T, w:Round(colW), h:B-T})
}
return rects
}
; "Quad": erstes Fenster = linke Haelfte (Master),
; die restlichen gestapelt in der rechten Haelfte.
; 2 Fenster -> zwei Haelften
; 3 Fenster -> links halb, rechts zwei Viertel
; 4 Fenster -> links halb, rechts drei Zeilen
LayGrid(n, a) {
global GAP_INNER, GAP_OUTER
rects := []
if (n = 0)
return rects
L := a.l+GAP_OUTER, T := a.t+GAP_OUTER, R := a.r-GAP_OUTER, B := a.b-GAP_OUTER
totalW := R - L, totalH := B - T
if (n = 1) {
rects.Push({x:L, y:T, w:totalW, h:totalH})
return rects
}
masterW := (totalW - GAP_INNER) / 2
rects.Push({x:L, y:T, w:Round(masterW), h:totalH}) ; Master links
stackN := n - 1
stackX := L + masterW + GAP_INNER
stackW := totalW - masterW - GAP_INNER
rowH := (totalH - (stackN-1)*GAP_INNER) / stackN
Loop stackN {
ry := T + (A_Index-1)*(rowH+GAP_INNER)
rects.Push({x:Round(stackX), y:Round(ry), w:Round(stackW), h:Round(rowH)})
}
return rects
}
; ══ Reihenfolge stabil halten ════════════════════════════════
; Bestehende Fenster behalten ihren Platz, neue kommen ans Ende,
; geschlossene fallen raus.
RebuildOrder() {
global TileOrder
managed := Map()
for h in WinGetList()
if IsManageable(h)
managed[h] := true
kept := []
for h in TileOrder
if managed.Has(h) {
kept.Push(h), managed.Delete(h)
}
for h in WinGetList() ; neue anhaengen
if managed.Has(h) {
kept.Push(h), managed.Delete(h)
}
TileOrder := kept
}
; Set-Signatur (sortiert) -> nur bei Fensterwechsel neu kacheln,
; nicht bei jedem Fokuswechsel
ManagedSig() {
s := ""
for h in WinGetList()
if IsManageable(h)
s .= h "`n"
return Sort(RTrim(s, "`n"), "N")
}
; ══ Kernaktion ═══════════════════════════════════════════════
Tile() {
global TileOrder, Tiling, Layout
if Tiling
return
Tiling := true
try {
RebuildOrder()
groups := Map() ; nach Monitor gruppieren
for h in TileOrder {
m := MonOf(h)
if !groups.Has(m)
groups[m] := []
groups[m].Push(h)
}
for m, list in groups {
wa := WorkArea(m)
n := list.Length
rects := (Layout = "grid") ? LayGrid(n, wa) : LayColumns(n, wa)
Loop n
PlaceWindow(list[A_Index], rects[A_Index].x, rects[A_Index].y
, rects[A_Index].w, rects[A_Index].h)
}
}
Tiling := false
}
; ══ Fenster tauschen (monitor-lokal, mit Umlauf) ═════════════
SwapFocused(dir) {
global TileOrder
hwnd := WinExist("A")
if !hwnd || !IsManageable(hwnd)
return
RebuildOrder()
hMon := MonOf(hwnd)
subIdx := [] ; Indizes dieses Monitors
for i, h in TileOrder
if (MonOf(h) = hMon)
subIdx.Push(i)
if (subIdx.Length < 2)
return
p := 0
for k, i in subIdx
if (TileOrder[i] = hwnd) {
p := k
break
}
if !p
return
q := Mod(p - 1 + dir + subIdx.Length, subIdx.Length) + 1 ; mit Umlauf
a := subIdx[p], b := subIdx[q]
tmp := TileOrder[a], TileOrder[a] := TileOrder[b], TileOrder[b] := tmp
Tile()
WinActivate(hwnd)
}
; ══ Auto-Retile-Schleife ═════════════════════════════════════
TileTick() {
global AutoTile, LastSig, Tiling
if !AutoTile || Tiling
return
if ((s := ManagedSig()) != LastSig) {
LastSig := s
Tile()
}
}
; ══ Steuerung ════════════════════════════════════════════════
StartTiling(mode) {
global Layout, AutoTile, LastSig
Layout := mode
AutoTile := true
LastSig := ManagedSig()
Tile()
SetTimer TileTick, 350 ; ab jetzt automatisch nachkacheln
Flash("Autotile AN — " (mode = "grid" ? "Quad" : "Spalten"))
}
StopTiling() {
global AutoTile
AutoTile := false
SetTimer TileTick, 0
Flash("Autotile AUS")
}
; ══════════════════════════════════════════════════════════════
; ══ TEIL 3: DARK/LIGHT-UMSCHALTER ══
; ══════════════════════════════════════════════════════════════
; Schaltet App- UND System-Theme um und benachrichtigt alle
; laufenden Programme (kein Neustart/Abmelden noetig).
ToggleTheme() {
key := "HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
cur := 1
try cur := RegRead(key, "AppsUseLightTheme")
val := cur ? 0 : 1 ; 0 = Dark, 1 = Light
try {
RegWrite val, "REG_DWORD", key, "AppsUseLightTheme"
RegWrite val, "REG_DWORD", key, "SystemUsesLightTheme"
} catch {
Flash("Theme-Umschaltung fehlgeschlagen (Registry)")
return
}
; WM_SETTINGCHANGE an alle Fenster -> Apps uebernehmen sofort
DllCall("SendMessageTimeout", "Ptr", 0xFFFF, "UInt", 0x1A
, "Ptr", 0, "Str", "ImmersiveColorSet"
, "UInt", 2, "UInt", 500, "Ptr*", 0) ; SMTO_ABORTIFHUNG
Flash(val ? "Theme: LIGHT" : "Theme: DARK")
}
; ══════════════════════════════════════════════════════════════
; ══ TEIL 4: ALWAYS ON TOP ══
; ══════════════════════════════════════════════════════════════
ToggleAOT(hwnd := 0) {
if !hwnd
hwnd := WinExist("A")
if !hwnd
return
isTop := WinGetExStyle(hwnd) & 0x8 ; WS_EX_TOPMOST
WinSetAlwaysOnTop -1, hwnd
Flash(isTop ? "Always on Top: AUS" : "Always on Top: AN")
}
OverTitleBar() {
MouseGetPos &mx, &my, &win
if !win
return false
lp := ((my & 0xFFFF) << 16) | (mx & 0xFFFF)
return SendMessage(0x0084, 0, lp, , "ahk_id " win) = 2 ; WM_NCHITTEST -> HTCAPTION
}
; ══════════════════════════════════════════════════════════════
; ══ TASTEN ══
; ══════════════════════════════════════════════════════════════
; ── Desktop-Leiste ein/aus (Notausgang) ──────────────────────
#0:: {
global Manual
Manual := !Manual
Flash("Desktop-Leiste: " (Manual ? "AN" : "AUS"))
Refresh(true)
}
; ── Desktop wechseln ─────────────────────────────────────────
#1::VDGo(1)
#2::VDGo(2)
#3::VDGo(3)
#4::VDGo(4)
#5::VDGo(5)
#6::VDGo(6)
#7::VDGo(7)
#8::VDGo(8)
#9::VDGo(9)
; ── Fenster verschieben (du bleibst) ─────────────────────────
#+1::MoveWin(1)
#+2::MoveWin(2)
#+3::MoveWin(3)
#+4::MoveWin(4)
#+5::MoveWin(5)
#+6::MoveWin(6)
#+7::MoveWin(7)
#+8::MoveWin(8)
#+9::MoveWin(9)
; ── Fenster verschieben UND mitspringen ──────────────────────
#!1::MoveWin(1, true)
#!2::MoveWin(2, true)
#!3::MoveWin(3, true)
#!4::MoveWin(4, true)
#!5::MoveWin(5, true)
#!6::MoveWin(6, true)
#!7::MoveWin(7, true)
#!8::MoveWin(8, true)
#!9::MoveWin(9, true)
; ── Autotile (Strg+Shift+Win + Buchstabe) ────────────────────
^+#z::StartTiling("columns") ; Spalten – Autotile an
^+#j::StartTiling("grid") ; Quad – Autotile an
^+#y::StopTiling() ; alles aus
^+#n::SwapFocused(1) ; aktives Fenster eine Position weiter
^+#b::SwapFocused(-1) ; ... und zurueck
; ── Dark/Light ───────────────────────────────────────────────
^+#o::ToggleTheme() ; Strg+Shift+Win+O
; ── Always on Top ────────────────────────────────────────────
^!t::ToggleAOT() ; Strg+Alt+T
#HotIf OverTitleBar()
+RButton:: {
MouseGetPos , , &win
ToggleAOT(win)
}
#HotIf