Last active
May 4, 2023 17:57
-
-
Save ristomatti/f075020025cf870aaf8f73a930996f14 to your computer and use it in GitHub Desktop.
AutoHotkey v1 script to mimic some aspects of tiling window managers in Windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Persistent | |
Menu, Tray, NoStandard | |
Menu, Tray, Add, Suspend, SuspendScript | |
Menu, Tray, Add, Exit, QuitScript | |
Menu, Tray, Tip, IonMove | |
Return | |
SuspendScript: | |
Suspend | |
Menu, Tray, ToggleCheck, Suspend | |
Return | |
QuitScript: | |
ExitApp | |
Return | |
#Left::ChangeFocus("left") | |
#Right::ChangeFocus("right") | |
#Up::ChangeFocus("up") | |
#Down::ChangeFocus("down") | |
#LButton::ClickControl("move") | |
#RButton::ClickControl("resize") | |
#MButton::ClickControl("fill") | |
; Param (dir) values: left, right, up, down | |
ChangeFocus(dir) { | |
; Set mouse move options | |
CoordMode, Mouse, Screen | |
SetMouseDelay, -1 | |
; Get position of active window and the list of open applications | |
WinGetPos, actX, actY, actW, actH, A | |
WinGet,AppList,List | |
; Variables for keeping the best match window | |
closeCoord = | |
closeId = | |
; Loop through list of open applications | |
Loop, %AppList% { | |
; Find the properties of current application on the list | |
curId := AppList%A_Index% | |
WinGetPos, curX,curY,curW,curH,ahk_id %curId% | |
WinGetTitle, curT,ahk_id %curId% | |
WinGet, curMin, MinMax, ahk_id %curId% | |
WinGetClass, curC, ahk_id %curId% | |
; Skip windows that are on top of each other | |
if (curX = actX && curY = actY && curW = actW && curH = actW) { | |
continue | |
} | |
; If title of the window is not empty and the window is not minimized | |
if ((curT <> "") && (curMin <> -1) && (curC <> "Shell_TrayWnd") && (curC <> "Progman")) { | |
if (dir = "left") { | |
if ((curX < actX) && (actY < curY+curH) && (curY < actY+actH) ) { | |
if ((curX > closeCoord) || (closeCoord = "")) { | |
closeCoord = %curX% | |
closeId = %curId% | |
} | |
} | |
} | |
else if (dir = "right") { | |
if ((curX > actX) && (actY < curY+curH) && (curY < actY+actH) ) { | |
if ((curX < closeCoord) || (closeCoord = "")) { | |
closeCoord = %curX% | |
closeId = %curId% | |
} | |
} | |
} | |
else if (dir = "up") { | |
if ((curY < actY) && (actX < curX+curW) && (curX < actX+actW)) { | |
if ((curY > closeCoord) || (closeCoord = "") || (curX = actX && curW = actW)) { | |
closeCoord = %curY% | |
closeId = %curId% | |
; Prioritize windows that are the same width and on the same x-coordinates | |
if (curX = actX && curW = actW && curY != actY) { | |
break | |
} | |
} | |
} | |
} | |
else if (dir = "down") { | |
if ((curY > actY) && (actX < curX+curW) && (curX < actX+actW)) { | |
if ((curY < closeCoord) || (closeCoord = "") || (curX = actX && curW = actW)) { | |
closeCoord = %curY% | |
closeId = %curId% | |
; Prioritize windows that are the same width and on the same x-coordinates | |
if (curX = actX && curW = actW && curY != actY) { | |
break | |
} | |
} | |
} | |
} | |
} | |
} | |
; If found a window to change the focus | |
if (closeId <> "") { | |
; First move the mouse over the center of the title | |
WinGetPos, actX,actY,actW,actH,ahk_id %closeId% | |
MouseMove, actX+actW/2,actY+13,0, | |
; Then activate the window | |
WinActivate, ahk_id %closeId% | |
WinSet, Top,,ahk_id %closeId% | |
} | |
} | |
ClickControl(function) { | |
;GroupAdd, CCIgnore, ahk_class | |
CoordMode, Mouse, Screen | |
MouseGetPos, mouseX, mouseY, id | |
WinGetPos, winX, winY, winW, winH, ahk_id %id% | |
WinSet, Top,, ahk_id %id% | |
WinGet, minMax, MinMax, ahk_id %id% | |
WinGetTitle, winT, ahk_id %id% | |
WinGetClass, winC, ahk_id %id% | |
; If window maximized, restore it | |
if (minMax == 1) { | |
WinRestore, ahk_id %id% | |
return | |
} | |
dir = | |
; If top half clicked | |
if ((mouseY > winY) && (mouseY < (winY+(winH/4)))) | |
dir = T | |
; If bottom half clicked | |
else if ((mouseY < (winY+winH)) && (mouseY > ((winY+winH)-(winH/4)))) | |
dir = B | |
; If left side clicked | |
if ((mouseX > winX) && (mouseX < (winX+(winW/4)))) | |
dir = %dir%L | |
; If right side clicked | |
else if ((mouseX < (winX+winW)) && (mouseX > ((winX+winW)-(winW/4)))) | |
dir = %dir%R | |
; Return if center of window clicked | |
if (dir = "") | |
dir = C | |
clickedMon = | |
SysGet, monCount, MonitorCount | |
Loop, %monCount% { | |
SysGet, monWorkArea, MonitorWorkArea, %A_Index% | |
if ((mouseX > monWorkAreaLeft) && (mouseX < monWorkAreaRight)) | |
clickedMon = %A_Index% | |
} | |
SysGet, monWorkArea, MonitorWorkArea, %clickedMon% | |
monWorkAreaWidth := monWorkAreaRight-monWorkAreaLeft | |
monWorkAreaHeigth := monWorkAreaBottom-monWorkAreaTop | |
if (function == "move") { | |
; Window movement | |
if (dir == "T") | |
WinMove, ahk_id %id%,,winX,monWorkAreaTop | |
if (dir == "B") | |
WinMove, ahk_id %id%,,winX,(monWorkAreaBottom-winH) | |
if (dir == "L") | |
WinMove, ahk_id %id%,,monWorkAreaLeft,winY | |
if (dir == "R") | |
WinMove, ahk_id %id%,,(monWorkAreaRight-winW),winY | |
if (dir == "TL") | |
WinMove, ahk_id %id%,,monWorkAreaLeft,monWorkAreaTop | |
if (dir == "TR") | |
WinMove, ahk_id %id%,,(monWorkAreaRight-winW),monWorkAreaTop | |
if (dir == "BL") | |
WinMove, ahk_id %id%,,monWorkAreaLeft,(monWorkAreaBottom-winH) | |
if (dir == "BR") | |
WinMove, ahk_id %id%,,(monWorkAreaRight-winW),(monWorkAreaBottom-winH) | |
; Center window | |
if (dir == "C") { | |
WinMove, ahk_id %id%,,((monWorkAreaLeft+(monWorkAreaWidth/2)) - (winW/2)),((monWorkAreaTop+(monWorkAreaHeigth/2)) - (winH/2)) | |
} | |
} | |
if (function == "resize") { | |
; If resize task, store previous state | |
if (dir != "C") { | |
global prevX := winX | |
global prevY := winY | |
global prevW := winW | |
global prevH := winH | |
global prevId := id | |
} | |
; Window resizing | |
if (dir == "T") | |
AltWinMove(winC,id,winX,monWorkAreaTop,winW,(winH+winY-monWorkAreaTop)) | |
if (dir == "B") | |
AltWinMove(winC,id,winX,winY,winW,(monWorkAreaBottom-winY)) | |
if (dir == "L") | |
AltWinMove(winC,id,monWorkAreaLeft,winY,(winW+winX-monWorkAreaLeft),winH) | |
if (dir == "R") | |
AltWinMove(winC,id,winX,winY,(monWorkAreaRight-winX),winH) | |
if (dir == "TL") | |
AltWinMove(winC,id,monWorkAreaLeft,monWorkAreaTop,(winW+winX-monWorkAreaLeft),(winH+winY-monWorkAreaTop)) | |
if (dir == "TR") | |
AltWinMove(winC,id,winX,monWorkAreaTop,(monWorkAreaRight-winX),(winH+winY-monWorkAreaTop)) | |
if (dir == "BL") | |
AltWinMove(winC,id,monWorkAreaLeft,winY,(winW+winX-monWorkAreaLeft),(monWorkAreaBottom-winY)) | |
if (dir == "BR") | |
AltWinMove(winC,id,winX,winY,(monWorkAreaRight-winX),(monWorkAreaBottom-winY)) | |
; Restore previous state | |
if (dir == "C") { | |
newW := monWorkAreaWidth/2 | |
newH := monWorkAreaHeigth/2 | |
AltWinMove(winC,id,((monWorkAreaLeft+(monWorkAreaWidth/2))-(newW/2)),((monWorkAreaTop+(monWorkAreaHeigth/2)) - (newH/2)),newW,newH) | |
; if (prevId == id) | |
; WinMove, ahk_id %id%,,prevX,prevY,prevW,prevH | |
} | |
} | |
; If fill available space mode function | |
if (function == "fill") { | |
global firstClick, selectedId, selectedC | |
; If first click (select) | |
if (firstClick == true || firstClick == "") { | |
; Don't select windows with no class or title or Progman | |
if (winC == "" || winT == "" || winC == "Progman") | |
return | |
selectedId := id | |
WinGetClass, selectedC, ahk_id %selectedId% | |
firstClick := false | |
return | |
} | |
; If second click (fill space) | |
if (firstClick == false) { | |
; If clicked on existing window, resize to that size | |
if (winC != "Progman" && id != selectedId) { | |
AltWinMove(selectedC, selectedId, winX, winY, winW, winH) | |
WinActivate, ahk_id %selectedId% | |
firstClick := true | |
} | |
else { | |
; Initially nearest coordinates are the current monitor work are | |
nearestTop := monWorkAreaTop | |
nearestBottom := monWorkAreaBottom | |
nearestLeft := monWorkAreaLeft | |
nearestRight := monWorkAreaRight | |
WinGet, appList,List | |
Loop, %appList% { | |
curId := appList%A_Index% | |
WinGetPos, curLeft,curTop,curW,curH,ahk_id %curId% | |
WinGetTitle, curT,ahk_id %curId% | |
WinGet, curMin, MinMax, ahk_id %curId% | |
WinGetClass, curC, ahk_id %curId% | |
curRight := curLeft+curW | |
curBottom := curTop+curH | |
; Skip window if not on the clicked monitor, is Progman or is minimized | |
if (curC == "Progman" || curC == "Shell_TrayWnd") | |
continue | |
if (curMin != 0) | |
continue | |
if (!(curLeft >= monWorkAreaLeft && (curLeft+curW) <= monWorkAreaRight && curTop >= monWorkAreaTop && (curTop+curH) <= monWorkAreaBottom)) | |
continue | |
if (curT == "" || curId == selectedId || curC = "TApplication") | |
continue | |
; Find nearest coordinates | |
if (curRight < mouseX && curBottom > mouseY) { | |
if (curRight > nearestLeft) | |
nearestLeft := curRight | |
} | |
if (curLeft > mouseX && curBottom > mouseY) { | |
if (curLeft < nearestRight) { | |
nearestRight := curLeft | |
} | |
} | |
if (curBottom < mouseY && curRight > mouseX) { | |
if (curBottom > nearestTop) | |
nearestTop := curBottom | |
} | |
if (curTop > mouseY && curLeft < mouseX && curRight > mouseX) { | |
if (curTop < nearestBottom) | |
nearestBottom := curTop | |
} | |
} | |
; If full screen available maximize instead of resize | |
if (nearestTop == monWorkAreaTop && nearestBottom == monWorkAreaBottom && nearestLeft == monWorkAreaLeft && nearestRight == monWorkAreaRight) { | |
WinMove, ahk_id %selectedId%,, nearestLeft, nearestTop | |
WinMaximize, ahk_id %selectedId% | |
} | |
else { | |
AltWinMove(selectedC, selectedId, nearestLeft, nearestTop, (nearestRight-nearestLeft), (nearestBottom-nearestTop)) | |
WinActivate, ahk_id %selectedId% | |
} | |
} | |
firstClick := true | |
return | |
} | |
} | |
} | |
AltWinMove(c, id, x, y, w, h) { | |
WM_ENTERSIZEMOVE = 0x231 | |
WM_EXITSIZEMOVE = 0x232 | |
if (c == "PuTTY" || c == "Nutty") { | |
WinActivate, ahk_class "Program" | |
SendMessage, 0x231, , , , ahk_id %id% | |
WinMove, ahk_id %id%,, x, y, w, h | |
SendMessage, 0x232, , , , ahk_id %id% | |
} | |
else { | |
WinMove, ahk_id %id%,, x, y, w, h | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment