Advertisement
pk3456

Window manager

Nov 15th, 2023 (edited)
1,104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Autohotkey 17.40 KB | Source Code | 0 0
  1. #Requires AutoHotkey v2.0
  2. #SingleInstance Force
  3.  
  4. ; Prevent capslock from turning on when using as a modifier.
  5. CapsLock::{
  6.     KeyWait "CapsLock"
  7.     if (A_ThisHotkey = "CapsLock")
  8.         SetCapsLockState(GetKeyState("CapsLock", "T") ? "AlwaysOff" : "AlwaysOn")
  9. }
  10.  
  11. class WindowManager {
  12.     static max_grid_size := 4
  13.     static modifier := "CapsLock"
  14.     static grid_line_color := 0x3CA4EA
  15.     static window_border_color := 0x237FD5
  16.  
  17.     static __New() {
  18.         this.current_col_count := 0
  19.         this.current_row_count := 0
  20.  
  21.         this.Overlay := this.GdipOverlay()
  22.         this.BackgroundBrush := this.GdipOverlay.Brush(140 << 24 | 0x0C0C0C)
  23.         this.GridBorderSize := 2
  24.         this.GridPen := this.GdipOverlay.Pen(200 << 24 | this.grid_line_color, this.GridBorderSize)
  25.         this.WindowBorderSize := 4
  26.         this.BorderPen := this.GdipOverlay.Pen(255 << 24 | this.window_border_color, this.WindowBorderSize)
  27.  
  28.         HotIf (*) => GetKeyState(this.modifier, "P")
  29.         ; modifier + key
  30.         Hotkey "*j", HotkeyCallback(ObjBindMethod(this, "MoveWindow", 1))      ; Move left
  31.         Hotkey "*i", HotkeyCallback(ObjBindMethod(this, "MoveWindow", 2))      ; Move up
  32.         Hotkey "*l", HotkeyCallback(ObjBindMethod(this, "MoveWindow", 3))      ; Move right
  33.         Hotkey "*k", HotkeyCallback(ObjBindMethod(this, "MoveWindow", 4))      ; Move down
  34.         Hotkey "*u", (*) => (Send("#+{Left}"), this.HideGuides())        ; move to previous monitor
  35.         Hotkey "*o", (*) => (Send("#+{Right}"), this.HideGuides())       ; move to next monitor
  36.         Hotkey "*m", ObjBindMethod(this, "Maximize") ; maximize
  37.         ; modifier + shift + key
  38.         Hotkey "*+j", HotkeyCallback(ObjBindMethod(this, "Resize", 1))   ; decrease width
  39.         Hotkey "*+i", HotkeyCallback(ObjBindMethod(this, "Resize", 2))   ; decrease height
  40.         Hotkey "*+l", HotkeyCallback(ObjBindMethod(this, "Resize", 3))   ; increase width
  41.         Hotkey "*+k", HotkeyCallback(ObjBindMethod(this, "Resize", 4))   ; increase height
  42.         ; modifier + alt + key
  43.         Hotkey "*!i", HotkeyCallback(ObjBindMethod(this, "ChangeRowCountAndAlign", -1))  ; increase row count
  44.         Hotkey "*!k", HotkeyCallback(ObjBindMethod(this, "ChangeRowCountAndAlign", 1))   ; decrease row count
  45.         Hotkey "*!j", HotkeyCallback(ObjBindMethod(this, "ChangeColCountAndAlign", -1))  ; decrease col count
  46.         Hotkey "*!l", HotkeyCallback(ObjBindMethod(this, "ChangeColCountAndAlign", 1))   ; increase col count
  47.  
  48.         HotIf ObjBindMethod(this, "GridIsVisible")
  49.         Hotkey "*" this.modifier " Up", (*) => this.HideGuides()
  50.  
  51.         HotIf ; End hotif
  52.  
  53.         HotkeyCallback(callback) {
  54.             ; If not excluded window, call the callback, and show guides if guides are not visible.
  55.             return (*) => (this.IsWindowExcluded() || (callback(coords := this.GetCoords()), this.GridIsVisible() || this.DrawGrid(coords)))
  56.         }
  57.         WM_DISPLAYCHANGE := 0x007E
  58.         OnMessage(WM_DISPLAYCHANGE, (*) => (this.Overlay.Hide(), this.Overlay := this.gdipOverlay()))
  59.     }
  60.  
  61.     static GridIsVisible(*) => DllCall("IsWindowVisible", "ptr", this.Overlay.hwnd)
  62.  
  63.     static IsWindowExcluded(win:="A") => InStr("(Shell_TrayWnd|Shell_SecondaryTrayWnd|WorkerW|AutoHotkeyGUI|XamlExplorerHostIslandWindow)", WinGetClass(win))
  64.  
  65.     static Maximize(*) {
  66.         this.current_col_count := this.current_row_count := 0
  67.         WinMaximize("A")
  68.         this.HideGuides()
  69.     }
  70.  
  71.     static ChangeColCountAndAlign(inc, coords) {
  72.         Critical
  73.         this.ChangeColCount(inc, coords)
  74.         coords := this.GetCoords(coords)
  75.         WinMoveEx(coords.X, coords.Y, coords.W, coords.H, "A")
  76.         this.DrawGrid(coords)
  77.     }
  78.  
  79.     static ChangeRowCountAndAlign(inc, coords) {
  80.         Critical
  81.         coords :=  this.ChangeRowCount(inc, coords)
  82.         WinMoveEx(coords.X, coords.Y, coords.W, coords.H, "A")
  83.         this.DrawGrid(coords)
  84.     }
  85.  
  86.     static ChangeColCount(inc, coords) {
  87.         this.current_col_count += inc
  88.         if this.current_col_count > this.max_grid_size
  89.             this.current_col_count := 1
  90.         if this.current_col_count < 1
  91.             this.current_col_count := this.max_grid_size
  92.         return this.GetCoords(coords)
  93.     }
  94.  
  95.     static ChangeRowCount(inc, coords) {
  96.         this.current_row_count += inc
  97.         if this.current_row_count > this.max_grid_size
  98.             this.current_row_count := 1
  99.         if this.current_row_count < 1
  100.             this.current_row_count := this.max_grid_size
  101.         return this.GetCoords(coords)
  102.     }
  103.  
  104.     static GetCoords(coords?) {
  105.         Critical
  106.         SetWinDelay(-1)
  107.         win := WinExist("A")
  108.         this.Mon := MonInfoFromWindow(win)
  109.  
  110.         if WinIsMax := (WinGetMinMax(win) = 1)
  111.             WinRestore(win)
  112.  
  113.         if !IsSet(coords) {
  114.             if WinIsMax
  115.                 winX := this.Mon.WALeft, winY := this.Mon.WATop, winW := this.Mon.WAWidth, winH := this.Mon.WAHeight
  116.             else
  117.                 WinGetPosEx(&winX, &winY, &winW, &winH, win)
  118.         } else {
  119.             winX := coords.X, winY := coords.Y, winW := coords.W, winH := coords.H
  120.         }
  121.  
  122.         ; If first run, find closest blockWidth and blockHeight to current window.
  123.         if this.current_col_count = 0 && this.current_row_count = 0 {
  124.             MonWidth := this.Mon.WAWidth
  125.             MonHeight := this.Mon.WAHeight
  126.  
  127.             lowest_diffX := MonWidth
  128.             lowest_diffY := MonHeight
  129.             lowest_diffW := MonWidth
  130.             lowest_diffH := MonHeight
  131.  
  132.             loop this.max_grid_size {
  133.                 blockWidth := MonWidth / A_Index
  134.                 blockHeight := MonHeight / A_Index
  135.  
  136.                 diffW := Abs(blockWidth - winW)
  137.                 diffH := Abs(blockHeight - winH)
  138.  
  139.                 if diffW <= lowest_diffW {
  140.                     lowest_diffW := diffW
  141.                     this.current_col_count := A_Index
  142.                     this.blockWidth := blockWidth
  143.                 }
  144.  
  145.                 if diffH <= lowest_diffH {
  146.                     lowest_diffH := diffH
  147.                     this.current_row_count := A_Index
  148.                     this.blockHeight := blockHeight
  149.                 }
  150.             }
  151.         } else {
  152.             this.blockWidth := this.Mon.WAWidth / this.current_col_count
  153.             this.blockHeight := this.Mon.WAHeight / this.current_row_count
  154.         }
  155.  
  156.         blockWidth := this.blockWidth
  157.         blockHeight := this.blockHeight
  158.  
  159.         ; Find closest X, Y, W, H in grid
  160.         X := Floor(WinX / blockWidth) * blockWidth
  161.         if Winx > X + blockWidth/2 ; If winX is greater than X + half of blockWidth, winX is closer to the right block than the left block
  162.             X += blockWidth        ; Move X one block right.
  163.         X := Min(Max(this.Mon.WALeft, X), this.Mon.WARight-blockWidth) ; Keep x position within monitor
  164.  
  165.         Y := Floor(WinY / blockHeight) * blockHeight
  166.         if WinY > Y + blockHeight/2
  167.             Y += blockHeight
  168.         Y := Min(Max(this.Mon.WATop, Y), this.Mon.WABottom-blockHeight)
  169.  
  170.         W := Ceil(winW / blockWidth) * blockWidth
  171.         if (X + W - blockWidth/2) > WinX+WinW
  172.             W -= blockWidth
  173.         W := Max(Min(W, this.Mon.WARight - X), blockWidth)
  174.  
  175.         H := Ceil(winH / blockHeight) * blockHeight
  176.         if (Y + H - blockHeight/2) > WinY+WinH
  177.             H -= blockHeight
  178.         H := Max(Min(H, this.Mon.WABottom - Y), blockHeight)
  179.  
  180.         tolerance := 4
  181.  
  182.         Edge := ""
  183.         Abs(X - this.Mon.WALeft) < tolerance         && Edge .= "L"
  184.         Abs(Y - this.Mon.WATop) < tolerance          && Edge .= "T"
  185.         Abs((this.Mon.WARight - W) - X) < tolerance  && Edge .= "R"
  186.         Abs((this.Mon.WABottom - H) - Y) < tolerance && Edge .= "B"
  187.  
  188.         Aligned := Abs(winX - X) < tolerance && Abs(winY - Y) < tolerance
  189.         Resized := Aligned && (W > this.blockWidth || H > this.blockHeight) && Abs(winW - W) < tolerance && Abs(winH - H) < tolerance
  190.  
  191.         return {
  192.             X:X,
  193.             Y:Y,
  194.             W:W,
  195.             H:H,
  196.             R:X+W,
  197.             B:Y+H,
  198.             Aligned:Aligned,
  199.             Resized:Resized,
  200.             Edge:Edge
  201.         }
  202.     }
  203.  
  204.     static MoveWindow(Dir, coords) {
  205.         Critical
  206.  
  207.         switch Dir {
  208.         case 1: ; Left
  209.             if coords.Aligned {
  210.                 if InStr(coords.Edge, "L")
  211.                     this.ChangeColCount(1, coords)
  212.                 coords.X -= this.blockWidth
  213.             }
  214.         case 2: ; Up
  215.             if coords.Aligned {
  216.                 if Instr(coords.Edge, "T")
  217.                     this.ChangeRowCount(1, coords)
  218.                 coords.Y -= this.blockHeight
  219.             }
  220.         case 3: ; Right
  221.             if coords.Aligned {
  222.                 if InStr(coords.Edge, "R")
  223.                     this.ChangeColCount(1, coords)
  224.                 coords.X += this.blockWidth
  225.                 if coords.Resized && InStr(coords.Edge, "R") && !InStr(coords.Edge, "L")
  226.                     coords.X -= (this.blockWidth/2)
  227.             }
  228.         case 4: ; Down
  229.             if coords.Aligned {
  230.                 if InStr(coords.Edge, "B")
  231.                     this.ChangeRowCount(1, coords)
  232.                 coords.Y += this.blockHeight
  233.                 if coords.Resized && InStr(coords.Edge, "B") && !InStr(coords.Edge, "T")
  234.                     coords.Y -= (this.blockHeight/2)
  235.             }
  236.         }
  237.         coords := this.GetCoords(coords)
  238.         WinMoveEx(coords.X, coords.Y, coords.W, coords.H, "A")
  239.         this.DrawGrid(coords)
  240.     }
  241.  
  242.     static Resize(dir, coords) {
  243.         Critical
  244.         switch dir {
  245.             case 1: ; Left
  246.                 coords.W -= this.blockWidth
  247.             case 2: ; Top
  248.                 coords.H -= this.blockHeight
  249.             case 3: ; Right
  250.                 coords.W += this.blockWidth
  251.             case 4: ; Down
  252.                 coords.H += this.blockHeight
  253.         }
  254.         coords := this.GetCoords(coords)
  255.         this.DrawGrid(coords)
  256.         WinMoveEx(coords.X, coords.Y, coords.W, coords.H, "A")
  257.     }
  258.  
  259.     static DrawGrid(coords) {
  260.         colLineCount := this.current_col_count + 1
  261.         rowLineCount := this.current_row_count + 1
  262.         RowsCompleted := colsCompleted := 0
  263.  
  264.         hdc := DllCall("CreateCompatibleDC", "ptr", 0)
  265.         ; Create a buffered bitmap to draw on
  266.         ; https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
  267.         NumPut("uint", 40, "uint", this.Mon.WAWidth, "uint", this.Mon.WAHeight, "ushort", 1, "ushort", 32, "uint", 0, bi := Buffer(40, 0))
  268.         hbm := DllCall("CreateDIBSection", "ptr", hdc, "ptr", bi, "uint", 0, "ptr*", &ppvBits:=0, "ptr", 0, "uint", 0, "ptr")
  269.         obm := DllCall("SelectObject", "ptr", hdc, "ptr", hbm)
  270.  
  271.         Graphics := this.GdipOverlay.GraphicsFromHDC(hdc)
  272.  
  273.         path := this.GdipOverlay.Path()
  274.         path.AddPathRectangle(0, 0, this.Mon.WAWidth, this.Mon.WAHeight)
  275.         path.AddPathRectangle(coords.X - this.Mon.WALeft, coords.Y - this.Mon.WATop, coords.W, coords.H)
  276.         Graphics.FillPath(this.BackgroundBrush, path)
  277.  
  278.         b := this.GridBorderSize
  279.         ; Draw grid
  280.         loop rowLineCount {
  281.             y := this.blockHeight * (A_Index - 1)
  282.             switch A_Index {
  283.             case 1            : y += b/2
  284.             case rowLineCount : y -= b/2
  285.             }
  286.             Graphics.DrawLine(this.GridPen, 0, this.Mon.WAWidth, y, y)
  287.         }
  288.  
  289.         loop colLineCount {
  290.             x := this.blockWidth * (A_Index - 1)
  291.             switch A_Index {
  292.                 case 1            : x += b/2
  293.                 case colLineCount : x -= b/2
  294.             }
  295.             Graphics.DrawLine(this.GridPen, x, x, 0, this.Mon.WAHeight)
  296.         }
  297.  
  298.         b := this.WindowBorderSize
  299.         ; Draw border around window.
  300.         Graphics.DrawRectangle(this.BorderPen, coords.X - this.Mon.WALeft+b/2, coords.Y - this.Mon.WATop+b/2, coords.W-b, coords.H-b)
  301.  
  302.         DllCall("UpdateLayeredWindow"
  303.             , "ptr", this.Overlay.hwnd
  304.             , "ptr", 0
  305.             , "ptr", 0
  306.             , "int64*", this.Mon.WAWidth|this.Mon.WAHeight<<32
  307.             , "ptr", hdc
  308.             , "int64*", 0
  309.             , "uint", 0
  310.             , "uint*", 255<<16|1<<24
  311.             , "uint", 2)
  312.  
  313.         this.Overlay.Show("NA x" this.Mon.WALeft " y" this.Mon.WATop " w" this.Mon.WAWidth " h" this.Mon.WAHeight)
  314.  
  315.         DllCall("SelectObject", "ptr", hdc, "ptr", obm) ; Select object back into the hdc
  316.         DllCall("DeleteObject", "ptr", hbm) ; Delete the bitmap
  317.         DllCall("DeleteDC", "ptr", hdc)
  318.     }
  319.  
  320.     static HideGuides(*) => this.Overlay.Hide()
  321.  
  322.     class GdipOverlay extends Gui {
  323.         __New() {
  324.             static module := 0
  325.             if !module && !DllCall("GetModuleHandle", "Str", "gdiplus", "ptr")
  326.                 module := DllCall("LoadLibrary", "str", "gdiplus")
  327.             NumPut("uint", 1, si := Buffer(A_PtrSize = 8 ? 24 : 16, 0))
  328.             DllCall("gdiplus\GdiplusStartup", "ptr*", &pToken:=0, "ptr", si, "uptr", 0)
  329.             if !pToken
  330.                 throw Error("Gdiplus failed to start. Please ensure you have gdiplus on your system")
  331.             this.gdipToken := pToken
  332.             super.__New("-Caption +E0x80000 +AlwaysOnTop +ToolWindow +E0x20")
  333.         }
  334.         __Delete() {
  335.             DllCall("gdiplus\GdiplusShutdown","ptr", this.gdipToken)
  336.         }
  337.  
  338.         class GraphicsFromHDC {
  339.             __New(hdc) {
  340.                 DllCall("gdiplus\GdipCreateFromHDC", "ptr", hdc, "uptr*", &pGraphics:=0)
  341.                 this.ptr := pGraphics
  342.             }
  343.             __Delete() {
  344.                 DllCall("gdiplus\GdipDeleteGraphics", "ptr", this)
  345.             }
  346.             DrawLine(pen, x1, x2, y1, y2) {
  347.                 DllCall("gdiplus\GdipDrawLine", "ptr", this, "ptr", pen, "float", x1, "float", y1, "float", x2, "float", y2)
  348.             }
  349.             DrawRectangle(pen, x, y, w, h) {
  350.                 DllCall("gdiplus\GdipDrawRectangle", "ptr", this, "ptr", pen, "float", x, "float", y, "float", w, "float", h)
  351.             }
  352.             FillRectangle(brush, x, y, w, h) {
  353.                 DllCall("gdiplus\GdipFillRectangle", "ptr", this, "ptr", brush, "float", x, "float", y, "float", w, "float", h)
  354.             }
  355.             FillPath(brush, path) {
  356.                 DllCall("gdiplus\GdipFillPath", "ptr", this, "ptr", brush, "ptr", path)
  357.             }
  358.         }
  359.  
  360.         class Brush {
  361.             __New(color:=0xFF000000) {
  362.                 DllCall("gdiplus\GdipCreateSolidFill", "uint", color, "ptr*", &pBrush:=0)
  363.                 this.ptr := pBrush
  364.             }
  365.             __Delete() => DllCall("gdiplus\GdipDeleteBrush", "ptr", this)
  366.         }
  367.  
  368.         class Pen {
  369.             __New(color:=0xFF000000, width:=1) {
  370.                 DllCall("gdiplus\GdipCreatePen1", "uint", color, "float", width, "int", 2, "ptr*", &pPen:=0)
  371.                 this.ptr := pPen
  372.             }
  373.             __Delete() => DllCall("gdiplus\GdipDeletePen", "ptr", this)
  374.         }
  375.  
  376.         class Path {
  377.             __New() {
  378.                 DllCall("gdiplus\GdipCreatePath", "int", 0, "ptr*", &pPath:=0)
  379.                 this.ptr := pPath
  380.             }
  381.             AddPathRectangle(x, y, w, h) {
  382.                 DllCall("gdiplus\GdipAddPathRectangle", "ptr", this, "float", x, "float", y, "float", w, "float", h)
  383.             }
  384.             __Delete() => DllCall("gdiplus\GdipDeletePath", "ptr", this)
  385.         }
  386.     }
  387. }
  388.  
  389. ; move window and fix offset from invisible border
  390. WinMoveEx(x?, y?, w?, h?, hwnd?) {
  391.     if !(hwnd is integer)
  392.         hwnd := WinExist(hwnd)
  393.     if !IsSet(hwnd)
  394.         hwnd := WinExist()
  395.  
  396.     ; compare pos and get offset
  397.     WinGetPosEx(&fX, &fY, &fW, &fH, hwnd)
  398.     WinGetPos(&wX, &wY, &wW, &wH, hwnd)
  399.     diffX := fX - wX
  400.     diffY := fY - wY
  401.     diffW := fW - wW
  402.     diffH := fH - wH
  403.     ; new x, y, w, h with offset corrected.
  404.     IsSet(x) && nX := x - diffX
  405.     IsSet(y) && nY := y - diffY
  406.     IsSet(w) && nW := w - diffW
  407.     IsSet(h) && nH := h - diffH
  408.     WinMove(nX?, nY?, nW?, nH?, hwnd?)
  409. }
  410.  
  411. ; get window position without the invisible border
  412. WinGetPosEx(&x?, &y?, &w?, &h?, hwnd?) {
  413.     static DWMWA_EXTENDED_FRAME_BOUNDS := 9
  414.  
  415.     if !(hwnd is integer)
  416.         hwnd := WinExist(hwnd)
  417.     if !IsSet(hwnd)
  418.         hwnd := WinExist() ; last found window
  419.  
  420.     DllCall("dwmapi\DwmGetWindowAttribute",
  421.             "ptr" , hwnd,
  422.             "uint", DWMWA_EXTENDED_FRAME_BOUNDS,
  423.             "ptr" , RECT := Buffer(16, 0),
  424.             "int" , RECT.size,
  425.             "uint")
  426.     x := NumGet(RECT,  0, "int")
  427.     y := NumGet(RECT,  4, "int")
  428.     w := NumGet(RECT,  8, "int") - x
  429.     h := NumGet(RECT, 12, "int") - y
  430. }
  431.  
  432. MonInfoFromWindow(hwnd) {
  433.     hMon := DllCall("MonitorFromWindow", "ptr", WinExist(hwnd), "uint", 2, "ptr")
  434.     NumPut("uint", 40, mi := Buffer(40))
  435.     DllCall("user32\GetMonitorInfo", "ptr", hMon, "ptr", mi)
  436.     return {Left     : L  := NumGet(mi,  4, "int")
  437.           , Top      : T  := NumGet(mi,  8, "int")
  438.           , Right    : R  := NumGet(mi, 12, "int")
  439.           , Bottom   : B  := NumGet(mi, 16, "int")
  440.           , WALeft   : WL := NumGet(mi, 20, "int")
  441.           , WATop    : WT := NumGet(mi, 24, "int")
  442.           , WARight  : WR := NumGet(mi, 28, "int")
  443.           , WABottom : WB := NumGet(mi, 32, "int")
  444.           , Width    : Width  := R - L
  445.           , Height   : Height := B - T
  446.           , WAWidth  : WR - WL
  447.           , WAHeight : WB - WT
  448.           , Primary  : NumGet(mi, 36, "uint")
  449.     }
  450. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement