Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 41 additions & 18 deletions ahk/Keys.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ SetWorkingDir A_ScriptDir
#Include %A_ScriptDir%\..\Lib\v2\AHK_Common.ahk
InitScript(true, false, false) ; UIA required, no admin, manual tuning

if (A_LineFile == A_ScriptFullPath) {
KeyHistory(0)
ListLines False
SetKeyDelay(-1, -1)
Expand Down Expand Up @@ -35,6 +36,7 @@ SetTimer(SetAlwaysOnTop, 1000)
; Tray documentation entry
A_TrayMenu.Add("Documentation", ShowDocumentation)

}
#HotIf !WinActive("ahk_exe Explorer.EXE")
+F1::WinMinimize("A")
#HotIf
Expand Down Expand Up @@ -85,40 +87,48 @@ AddDateToSelection() {
}
}

MoveWindowLeft() {
MoveWindowLeft(api := "") {
global lw, mh
SaveWindowPosition()
if IsResizable() {
WinMove(0, 0, lw, mh, "A")
if !api
api := KeysWindowAPI()
SaveWindowPosition(api)
if IsResizable(api) {
api.WinMove(0, 0, lw, mh, "A")
} else {
WinMove(0, 0, , , "A")
api.WinMoveNoSize(0, 0, "A")
}
}

MoveWindowRight() {
MoveWindowRight(api := "") {
global lw, rw, mh
SaveWindowPosition()
if IsResizable() {
WinMove(lw, 0, rw, mh, "A")
if !api
api := KeysWindowAPI()
SaveWindowPosition(api)
if IsResizable(api) {
api.WinMove(lw, 0, rw, mh, "A")
} else {
WinMove(lw, 0, , , "A")
api.WinMoveNoSize(lw, 0, "A")
}
}

RestoreWindowPosition() {
RestoreWindowPosition(api := "") {
global positions
hwnd := WinExist("A")
if !api
api := KeysWindowAPI()
hwnd := api.WinExist("A")
if !positions.Has(hwnd)
return

pos := positions[hwnd]
WinMove(pos[1], pos[2], pos[3], pos[4], "A")
api.WinMove(pos[1], pos[2], pos[3], pos[4], "A")
}

SaveWindowPosition() {
SaveWindowPosition(api := "") {
global positions
hwnd := WinExist("A")
WinGetPos(&x, &y, &w, &h, "A")
if !api
api := KeysWindowAPI()
hwnd := api.WinExist("A")
api.WinGetPos(&x, &y, &w, &h, "A")
positions[hwnd] := [x, y, w, h]
}

Expand All @@ -132,8 +142,10 @@ GetMonitorHeight() {
return bottom - top
}

IsResizable() {
style := WinGetStyle("A")
IsResizable(api := "") {
if !api
api := KeysWindowAPI()
style := api.WinGetStyle("A")
return (style & 0x00040000) != 0 ; WS_SIZEBOX
}

Expand Down Expand Up @@ -203,3 +215,14 @@ ShowDocumentation(*) {
tabs.UseTab()
docGui.Show("Center")
}




class KeysWindowAPI {
WinExist(winTitle) => WinExist(winTitle)
WinGetPos(&x, &y, &w, &h, winTitle) => WinGetPos(&x, &y, &w, &h, winTitle)
WinGetStyle(winTitle) => WinGetStyle(winTitle)
WinMove(x, y, w, h, winTitle) => WinMove(x, y, w, h, winTitle)
WinMoveNoSize(x, y, winTitle) => WinMove(x, y, , , winTitle)
}
195 changes: 195 additions & 0 deletions ahk/test_Keys.ahk
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#Requires AutoHotkey v2.0
#SingleInstance Force

#Include %A_ScriptDir%\..\Lib\v2\AHK_Common.ahk

InitScript(false, false, false)

; Define the test mock variables used by Keys.ahk logic
global positions := Map()
global lw := 800
global rw := 900
global mh := 1000
global mw := 1920

; Override API for test
class MockKeysAPI {
__New() {
this.calls := []
this.mockStyle := 0
this.mockHwnd := 12345
this.mockPos := {x: 10, y: 20, w: 800, h: 600}
}

WinExist(winTitle) {
this.calls.Push({method: "WinExist", args: [winTitle]})
return this.mockHwnd
}

WinGetPos(&x, &y, &w, &h, winTitle) {
this.calls.Push({method: "WinGetPos", args: [winTitle]})
x := this.mockPos.x
y := this.mockPos.y
w := this.mockPos.w
h := this.mockPos.h
}

WinGetStyle(winTitle) {
this.calls.Push({method: "WinGetStyle", args: [winTitle]})
return this.mockStyle
}

WinMove(x, y, w, h, winTitle) {
this.calls.Push({method: "WinMove", args: [x, y, w, h, winTitle]})
}

WinMoveNoSize(x, y, winTitle) {
this.calls.Push({method: "WinMoveNoSize", args: [x, y, winTitle]})
}
}

stdout := FileOpen("*", "w `n")
testsPassed := 0
testsFailed := 0

AssertEqual(expected, actual, context) {
global testsPassed, testsFailed, stdout

expectedStr := IsObject(expected) ? ObjToString(expected) : expected
actualStr := IsObject(actual) ? ObjToString(actual) : actual

if (expectedStr == actualStr) {
testsPassed++
stdout.WriteLine("PASS: " . context)
} else {
testsFailed++
stdout.WriteLine("FAIL: " . context . " - Expected '" . expectedStr . "', but got '" . actualStr . "'")
}
}

ObjToString(obj) {
if !IsObject(obj)
return obj
if (Type(obj) == "Array") {
str := "["
for v in obj
str .= ObjToString(v) . ", "
return RTrim(str, ", ") . "]"
}
return Type(obj)
}

; Include the actual Keys.ahk so we can call the functions
#Include %A_ScriptDir%\Keys.ahk

try {
stdout.WriteLine("Starting tests for Keys.ahk window functions...")

; Test SaveWindowPosition
global positions := Map()
api := MockKeysAPI()
api.mockHwnd := 9999
api.mockPos := {x: 50, y: 60, w: 1024, h: 768}

SaveWindowPosition(api)

AssertEqual(true, positions.Has(9999), "SaveWindowPosition should store entry for hwnd")
if (positions.Has(9999)) {
AssertEqual([50, 60, 1024, 768], positions[9999], "SaveWindowPosition should save correct coordinates")
}

; Test IsResizable
api := MockKeysAPI()
api.mockStyle := 0x00000000
AssertEqual(false, IsResizable(api), "IsResizable should return false without WS_SIZEBOX")

api.mockStyle := 0x00040000
AssertEqual(true, IsResizable(api), "IsResizable should return true with WS_SIZEBOX")

; Test RestoreWindowPosition
global positions := Map()
positions[7777] := [100, 200, 800, 600]
api := MockKeysAPI()
api.mockHwnd := 7777

RestoreWindowPosition(api)

AssertEqual("WinMove", api.calls[2].method, "RestoreWindowPosition should call WinMove")
if (api.calls.Length >= 2) {
AssertEqual([100, 200, 800, 600, "A"], api.calls[2].args, "RestoreWindowPosition should move to saved pos")
}

; Test RestoreWindowPosition - No saved position
api := MockKeysAPI()
api.mockHwnd := 8888

RestoreWindowPosition(api)
AssertEqual(1, api.calls.Length, "RestoreWindowPosition should not call WinMove if not saved")

; Test MoveWindowLeft (Resizable)
global positions := Map()
api := MockKeysAPI()
api.mockHwnd := 1111
api.mockStyle := 0x00040000 ; Resizable

global lw := 800
global mh := 1000

MoveWindowLeft(api)

AssertEqual(true, positions.Has(1111), "MoveWindowLeft should save position first")
AssertEqual("WinMove", api.calls[4].method, "MoveWindowLeft should call WinMove")
if (api.calls.Length >= 4) {
AssertEqual([0, 0, 800, 1000, "A"], api.calls[4].args, "MoveWindowLeft should move to left half")
}


; Test MoveWindowLeft (Not Resizable)
global positions := Map()
api := MockKeysAPI()
api.mockHwnd := 3333
api.mockStyle := 0 ; Not resizable

global lw := 800
global mh := 1000

MoveWindowLeft(api)

AssertEqual(true, positions.Has(3333), "MoveWindowLeft should save position first")
AssertEqual("WinMoveNoSize", api.calls[4].method, "MoveWindowLeft should call WinMoveNoSize")
if (api.calls.Length >= 4) {
AssertEqual([0, 0, "A"], api.calls[4].args, "MoveWindowLeft non-resizable should only move x,y")
}

; Test MoveWindowRight (Not Resizable)
global positions := Map()
api := MockKeysAPI()
api.mockHwnd := 2222
api.mockStyle := 0 ; Not resizable

global lw := 800
global rw := 900
global mh := 1000

MoveWindowRight(api)

AssertEqual(true, positions.Has(2222), "MoveWindowRight should save position first")
AssertEqual("WinMoveNoSize", api.calls[4].method, "MoveWindowRight should call WinMoveNoSize")
if (api.calls.Length >= 4) {
AssertEqual([800, 0, "A"], api.calls[4].args, "MoveWindowRight non-resizable should only move x,y")
}

stdout.WriteLine("---")
stdout.WriteLine("Tests Passed: " . testsPassed)
stdout.WriteLine("Tests Failed: " . testsFailed)

} catch as e {
stdout.WriteLine("Error during tests: " e.Message)
testsFailed++
}

if (testsFailed > 0) {
ExitApp(1)
}

ExitApp(0)
Loading