Simple Windows Drag - Easily snap, move and resize windows

Post your working scripts, libraries and tools for AHK v1.1 and older
Tome Sulmont
Posts: 2
Joined: 28 Dec 2022, 06:44

Simple Windows Drag - Easily snap, move and resize windows

25 Feb 2023, 17:28

The goal is to create a script that makes it easy to snap, move and resize windows with a single keystroke and mouse movements.

I’v tried to make those 3 scripts work together :
- Easy Window Dragging KDE style - by Jonny https://www.autohotkey.com/docs/v1/scripts/index.htm#EasyWindowDrag_(KDE)
- Hyper Window Snap - by Andrew Moore and Jeff Axelrod https://github.com/glenviewjeff/HyperWindowSnap
- Middle Mouse Button Gestures (diagonal) - by Drozdman https://www.autohotkey.com/board/topic/87331-middle-mouse-button-gestures-diagonal

It’s all written in Autohotkey V1.1 because of the amount of ressources online and the fact that the originals scripts are all written in this version of the language.
I’m new to coding, so my code is probably not well optimized. But overall, I’m pretty happy with what this piece of code can do.

I’ve created a gitub repository for the project, where you can find more explainations and to make it colaborative !

The link : https://github.com/Tome-Sulmont/SimpleWindowsDrag

[Mod edit: Topic moved from 'Scripts and Functions'.]
User avatar
mikeyww
Posts: 27150
Joined: 09 Sep 2014, 18:38

Re: Simple Windows Drag - Easily snap, move and resize windows

25 Feb 2023, 18:56

Welcome to this AutoHotkey forum!

This board is for people who need help with their scripts. There is a separate board for working scripts and functions.
gregster
Posts: 9074
Joined: 30 Sep 2013, 06:48

Re: Simple Windows Drag - Easily snap, move and resize windows

26 Feb 2023, 05:51

mikeyww wrote:
25 Feb 2023, 18:56
This board is for people who need help with their scripts. There is a separate board for working scripts and functions.
Yes, originalIy I found this post in the v2 scripts and functions forum. Could probably be moved to v1 scripts and functions, based on the description that it is working already.
Edit: Moved.
User avatar
RDC
Posts: 112
Joined: 29 Jan 2023, 10:22

Re: Simple Windows Drag - Easily snap, move and resize windows

26 Feb 2023, 06:37

But overall, I’m pretty happy with what this piece of code can do.
As you should be. This works quite well, although I had to switch F14 to F12 as my keyboard doesn't go above that.
If it had a way to lock a window in place it would be perfect for my needs. Alas, all known methods apparently don't work with remote (rdp) windows.
This one will be added to my collection for potential alterations down the road, so thank you!!
Tome Sulmont
Posts: 2
Joined: 28 Dec 2022, 06:44

Re: Simple Windows Drag - Easily snap, move and resize windows

11 Mar 2023, 17:31

Thanks for moving the thread !

@RDC Feel free to share if you find a way to lock the windows in place, I'm interested.
afshindavoudy
Posts: 44
Joined: 10 Jan 2024, 13:25

Re: Simple Windows Drag - Easily snap, move and resize windows

22 Feb 2024, 15:14

# Update
Here is the v2 version for those who interested:

Code: Select all

; REMOVED: #NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode("Input")  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir(A_ScriptDir)  ; Ensures a consistent starting directory.
#SingleInstance Force
; TraySetIcon("imageres.dll", "262") ; makes the icon into two window


; Simple Window Drag 
; --------------------------------------------------
; version : 1.0.0
; -> Script by Tomé Sulmont (tome.sulmont@laposte.net)
;
; Includes modified versions of :
; 	- Easy Window Dragging KDE style - by Jonny
; 	- Middle Mouse Button Gestures (diagonal) - by Drozdman
;	- Hyper Window Snap - by Andrew Moore and Jeff Axelrod
;
; https://www.autohotkey.com
;
; The goal with this script is to easily snap, move and resize windows with a single
; key press and a mouse :
;   1) Hold down the LWin key and LEFT-click anywhere inside a window to drag it to
;   a new location.
; 	2) Hold down LWin and RIGHT-click-drag anywhere inside a window to easily resize it.
;   3) Hold down LWin and MIDDLE-click-drag to perform different actions : maximize,
;   always on top or snap a window.
; 	4) Hold down LWin and MIDDLE-click without moving to minimize the window under
;   the mouse cursor.
;
; Shortcuts :
;  LWin + Left Button   + Drag					    : Move a window.
;  LWin + Right Button  + Drag						: Resize a window.
;  LWin + Middle Button + Drag Up					: Maximize/Restore a window.
;  LWin + Middle Button + Drag Down 					: Always On Top a window.
;  LWin + Middle Button + Drag Right 				: Snap to the Right half.
;  LWin + Middle Button + Drag Left 					: Snap to the Left half.
;  LWin + Middle Button + Drag Top Right Corner 		: Snap to the Top-Right quarter.
;  LWin + Middle Button + Drag Top Left Corner 		: Snap to the Top-Left quarter.
;  LWin + Middle Button + Drag Bottom Right Corner 	: Snap to Bottom-Right quarter.
;  LWin + Middle Button + Drag Bottom Left Corner 	: Snap to the Bottom-Left quarter.
;  LWin + Middle Button + Motionless 				: Minimize a window.
;  LWin + Numpad                                     : Snap the active window.

; Note
; --------------------------------------------------
; In my case, LWin is remapped to Caps Lock with PowerToy (because "Capslock::LWin" doesn't work)
If (VerCompare(A_AhkVersion, "1.0.39.00") < 0) {
    msgResult := MsgBox("This script may not work properly with your version of AutoHotkey. Continue?", "", 20)
    if (msgResult = "No")
        ExitApp()
}


; Easy Window Dragging
; --------------------------------------------------
; This is the setting that runs smoothest on my system. Depending on your video card and cpu power, you may want to raise or lower this value.
SetWinDelay(2)
CoordMode("Mouse")
return


; Mouse Gesture
; --------------------------------------------------
LWin & LButton:: {
    MouseGetPos(, , &KDE_id) ; Get the window id
    ; Abort if it's the desktop.
    ClassWin := WinGetClass("ahk_id " KDE_id)
    if (ClassWin = "WorkerW")
        return
    ; If the window is maximized, restore the initial position
    ; and size of the window and center the mouse.
    KDE_Win := WinGetMinMax("ahk_id " KDE_id)
    If KDE_Win {
        WinRestore("ahk_id " KDE_id)
        WinGetPos(&KDE_WinX1, &KDE_WinY1, &KDE_WinW, &KDE_WinH, "ahk_id " KDE_id)
        MouseMove((KDE_WinX1 + (KDE_WinW / 2)), (KDE_WinY1 + (KDE_WinH / 2)), 0)
    }

    ; Move the window.
    MouseGetPos(&KDE_X1, &KDE_Y1)
    WinGetPos(&KDE_WinX1, &KDE_WinY1, , , "ahk_id " KDE_id)
    Loop {
        KDE_Button := GetKeyState("LButton", "P") ? "D" : "U" ; Break if button has been released.
        if (KDE_Button = "U")
            break
        MouseGetPos(&KDE_X2, &KDE_Y2) ; Get the current mouse position.
        KDE_X2 -= KDE_X1 ; Obtain an offset from the initial mouse position.
        KDE_Y2 -= KDE_Y1
        KDE_WinX2 := (KDE_WinX1 + KDE_X2) ; Apply this offset to the window position.
        KDE_WinY2 := (KDE_WinY1 + KDE_Y2)
        WinMove(KDE_WinX2, KDE_WinY2, , , "ahk_id " KDE_id) ; Move the window to the new position.
    }
    return
}
LWin & RButton:: {
    ; Get the mouse position and window id,
    MouseGetPos(&KDE_X1, &KDE_Y1, &KDE_id)
    ; Abort if it's the desktop.
    ClassWin := WinGetClass("ahk_id " KDE_id)
    if (ClassWin = "WorkerW")
        return
    ; Abort if the window is maximized.
    KDE_Win := WinGetMinMax("ahk_id " KDE_id)
    If KDE_Win
        return
    ; Get the initial window position and size.
    WinGetPos(&KDE_WinX1, &KDE_WinY1, &KDE_WinW, &KDE_WinH, "ahk_id " KDE_id)
    ; Define the window region the mouse is currently in.
    ; The four regions are Up and Left, Up and Right, Down and Left, Down and Right.
    If (KDE_X1 < KDE_WinX1 + KDE_WinW / 2)
        KDE_WinLeft := 1
    Else
        KDE_WinLeft := -1
    If (KDE_Y1 < KDE_WinY1 + KDE_WinH / 2)
        KDE_WinUp := 1
    Else
        KDE_WinUp := -1
    Loop {
        KDE_Button := GetKeyState("RButton", "P") ? "D" : "U" ; Break if button has been released.
        if (KDE_Button = "U")
            break
        MouseGetPos(&KDE_X2, &KDE_Y2) ; Get the current mouse position.
        ; Get the current window position and size.
        WinGetPos(&KDE_WinX1, &KDE_WinY1, &KDE_WinW, &KDE_WinH, "ahk_id " KDE_id)
        KDE_X2 -= KDE_X1 ; Obtain an offset from the initial mouse position.
        KDE_Y2 -= KDE_Y1
        ; Then, act according to the defined region.
        WinMove(KDE_WinX1 + (KDE_WinLeft + 1) / 2 * KDE_X2, KDE_WinY1 + (KDE_WinUp + 1) / 2 * KDE_Y2, KDE_WinW - KDE_WinLeft * KDE_X2, KDE_WinH - KDE_WinUp * KDE_Y2, "ahk_id " KDE_id)  ; X of resized window
        KDE_X1 := (KDE_X2 + KDE_X1) ; Reset the initial position for the next iteration.
        KDE_Y1 := (KDE_Y2 + KDE_Y1)
    }
    return
}
LWin & MButton:: {
    MouseGetPos(&X1, &Y1, &KDE_id) ; Get the window id
    ; Abort if it's the desktop.
    ClassWin := WinGetClass("ahk_id " KDE_id)
    if (ClassWin = "WorkerW")
        return
    KeyWait("MButton")
    MouseGetPos(&X2, &Y2)
    MinTime := 50 ; Set` A_TimeSinceThisHotkey
    LongTime := 200 ; Set A_TimeSinceThisHotkey to avoid hiting Gesture down by accident
    Tolerance := 60 ; Tolerance of the gesture
    if (A_TimeSinceThisHotkey > MinTime AND X2 - X1 > 0 AND Y2 - Y1 < 0 AND Abs(X2 - X1) >= Tolerance AND Abs(Y2 - Y1) >= Tolerance) {
        GestureTR(KDE_id)	; gesture top/right
    }
    else if (A_TimeSinceThisHotkey > MinTime AND X2 - X1 > 0 AND Y2 - Y1 > 0 AND Abs(X2 - X1) >= Tolerance AND Abs(Y2 - Y1) >= Tolerance) {
        GestureDR(KDE_id)	; gesture down/right
    }
    else if (A_TimeSinceThisHotkey > MinTime AND X2 - X1 < 0 AND Y2 - Y1 < 0 AND Abs(X2 - X1) >= Tolerance AND Abs(Y2 - Y1) >= Tolerance) {
        GestureTL(KDE_id)	; gesture top/left
    }
    else if (A_TimeSinceThisHotkey > MinTime AND X2 - X1 < 0 AND Y2 - Y1 > 0 AND Abs(X2 - X1) >= Tolerance AND Abs(Y2 - Y1) >= Tolerance) {
        GestureDL(KDE_id)	; gesture down/left
    }
    else if (A_TimeSinceThisHotkey > MinTime AND Y2 - Y1 < 0 AND Abs(Y2 - Y1) >= Tolerance AND Abs(X2 - X1) < Tolerance) {
        GestureU(KDE_id)	; gesture up
    }
    else if (A_TimeSinceThisHotkey > LongTime AND Y2 - Y1 > 0 AND Abs(Y2 - Y1) >= Tolerance AND Abs(X2 - X1) < Tolerance) {
        GestureD(KDE_id)	; Gesture down
    }
    else if (A_TimeSinceThisHotkey > MinTime AND X2 - X1 < 0 AND Abs(X2 - X1) >= Tolerance AND Abs(Y2 - Y1) < Tolerance) {
        GestureL(KDE_id)	; gesture left
    }
    else if (A_TimeSinceThisHotkey > MinTime AND X2 - X1 > 0 AND Abs(X2 - X1) >= Tolerance AND Abs(Y2 - Y1) < Tolerance) {
        GestureR(KDE_id)	; gesture right
    }
    else if ((X1 = X2) and (Y1 = Y2)) {
        NoMove(KDE_id)		; gesture no move
    }
    return
}


; Hyper Window Snap
; --------------------------------------------------
SnapActiveWindow(winPlaceVertical, winPlaceHorizontal, winSizeHeight) {
    heightOffset := 7
    widthOffset := 15
    xOffset := 7

    activeWin := WinExist("A")
    activeMon := GetMonitorIndexFromWindow(activeWin)
    MinMaxState := WinGetMinMax("A")
    If (MinMaxState) {
        WinRestore("A")
    }

    MonitorGetWorkArea(activeMon, &MonitorWorkAreaLeft, &MonitorWorkAreaTop, &MonitorWorkAreaRight, &MonitorWorkAreaBottom)

    if (winSizeHeight == "half") {
        height := (MonitorWorkAreaBottom - MonitorWorkAreaTop) / 2 + heightOffset
    } else if (winSizeHeight == "full") {
        height := (MonitorWorkAreaBottom - MonitorWorkAreaTop) + heightOffset
    } else if (winSizeHeight == "third") {
        height := (MonitorWorkAreaBottom - MonitorWorkAreaTop) / 3
    }

    if (winPlaceHorizontal == "left") {
        posX := MonitorWorkAreaLeft - xOffset
        width := (MonitorWorkAreaRight - MonitorWorkAreaLeft) / 2 + widthOffset
    } else if (winPlaceHorizontal == "right") {
        posX := MonitorWorkAreaLeft + (MonitorWorkAreaRight - MonitorWorkAreaLeft) / 2 - xOffset
        width := (MonitorWorkAreaRight - MonitorWorkAreaLeft) / 2 + widthOffset
    } else {
        posX := MonitorWorkAreaLeft - xOffset
        width := MonitorWorkAreaRight - MonitorWorkAreaLeft + widthOffset
    }

    if (winPlaceVertical == "bottom") {
        posY := MonitorWorkAreaBottom - height + heightOffset
    } else if (winPlaceVertical == "middle") {
        posY := MonitorWorkAreaTop + height
    } else {
        posY := MonitorWorkAreaTop
    }

    WinMove(posX, posY, width, height, "A")
}

GetMonitorIndexFromWindow(windowHandle) {
    ; Starts with 1.
    monitorIndex := 1

    monitorInfo := Buffer(40)
    NumPut('UInt', 40, monitorInfo)
    

    if (monitorHandle := DllCall("MonitorFromWindow", "Ptr", windowHandle, "UInt", 0x2))
        && DllCall("GetMonitorInfo", "Ptr", monitorHandle, "Ptr", monitorInfo) {
            monitorLeft := NumGet(monitorInfo, 4, "Int")
            monitorTop := NumGet(monitorInfo, 8, "Int")
            monitorRight := NumGet(monitorInfo, 12, "Int")
            monitorBottom := NumGet(monitorInfo, 16, "Int")
            workLeft := NumGet(monitorInfo, 20, "Int")
            workTop := NumGet(monitorInfo, 24, "Int")
            workRight := NumGet(monitorInfo, 28, "Int")
            workBottom := NumGet(monitorInfo, 32, "Int")
            isPrimary := NumGet(monitorInfo, 36, "UInt") & 1

            monitorCount := MonitorGetCount()

            Loop monitorCount {
                MonitorGet(A_Index, &tempMonLeft, &tempMonTop, &tempMonRight, &tempMonBottom)

                ; Compare location to determine the monitor index.
                if ((monitorLeft = tempMonLeft) and (monitorTop = tempMonTop)
                    and (monitorRight = tempMonRight) and (monitorBottom = tempMonBottom)) {
                        monitorIndex := A_Index
                        break
                }
            }
    }

    return monitorIndex
}


; Set actions
; --------------------------------------------------
; Add Title Id to diferanciate diferent multiple instances (explorer or chrome for exemple) ?
GestureTR(KDE_id) {	; gesture top/right
    WinActivate("ahk_id " KDE_id) ; Activate the window
    SnapActiveWindow("top", "right", "half")
    return
}
GestureDR(KDE_id) {	; gesture down/right
    WinActivate("ahk_id " KDE_id) ; Activate the window
    SnapActiveWindow("bottom", "right", "half")
    return
}
GestureTL(KDE_id) {	; gesture top/left
    WinActivate("ahk_id " KDE_id) ; Activate the window
    SnapActiveWindow("top", "left", "half")
    return
}
GestureDL(KDE_id) {	; gesture down/left
    WinActivate("ahk_id " KDE_id) ; Activate the window
    SnapActiveWindow("bottom", "left", "half")
    return
}
GestureU(KDE_id) {	; gesture up
    ; Toggle between maximized and restored state.
    KDE_Win := WinGetMinMax("ahk_id " KDE_id)
    If KDE_Win
        WinRestore("ahk_id " KDE_id)
    Else
        WinMaximize("ahk_id " KDE_id)
    return
}
GestureD(KDE_id) {	; Gesture down
    WinActivate("KDE_id") ; Activate the window
    WinSetAlwaysOnTop(-1, "ahk_id " KDE_id)
    return
}
GestureL(KDE_id) {	; gesture left
    WinActivate("ahk_id " KDE_id) ; Activate the window
    SnapActiveWindow("top", "left", "full")
    return
}
GestureR(KDE_id) { ; gesture right
    WinActivate("ahk_id " KDE_id) ; Activate the window
    SnapActiveWindow("top", "right", "full")
    return
}
NoMove(KDE_id) { ; gesture no move
    WinMinimize("ahk_id " KDE_id) ; Minimize the window
    return
}


; Win + Numpad = Snap to conrners for diagonals, or top, bottom, left, right of screen (Landscape)
; --------------------------------------------------
LWin & Numpad1:: {
    ClassWin := WinGetClass("A") ; Abort if it's the desktop.
    if (ClassWin = "WorkerW")
        return
    SnapActiveWindow("bottom", "left", "half")
    return
}
LWin & Numpad2:: {
    ClassWin := WinGetClass("A") ; Abort if it's the desktop.
    if (ClassWin = "WorkerW")
        return
    SnapActiveWindow("bottom", "full", "half")
    return
}
LWin & Numpad3:: {
    ClassWin := WinGetClass("A") ; Abort if it's the desktop.
    if (ClassWin = "WorkerW")
        return
    SnapActiveWindow("bottom", "right", "half")
    return
}
LWin & Numpad4:: {
    ClassWin := WinGetClass("A") ; Abort if it's the desktop.
    if (ClassWin = "WorkerW")
        return
    SnapActiveWindow("top", "left", "full")
    return
}
LWin & Numpad5:: {
    ClassWin := WinGetClass("A") ; Abort if it's the desktop.
    if (ClassWin = "WorkerW")
        return
    KDE_Win := WinGetMinMax("A")
    If KDE_Win
        WinRestore("A")
    Else
        WinMaximize("A")
    return
}
LWin & Numpad6:: {
    ClassWin := WinGetClass("A") ; Abort if it's the desktop.
    if (ClassWin = "WorkerW")
        return
    SnapActiveWindow("top", "right", "full")
    return
}
LWin & Numpad7:: {
    ClassWin := WinGetClass("A") ; Abort if it's the desktop.
    if (ClassWin = "WorkerW")
        return
    SnapActiveWindow("top", "left", "half")
    return
}
LWin & Numpad8:: {
    ClassWin := WinGetClass("A") ; Abort if it's the desktop.
    if (ClassWin = "WorkerW")
        return
    SnapActiveWindow("top", "full", "half")
    return
}
LWin & Numpad9:: {
    ClassWin := WinGetClass("A") ; Abort if it's the desktop.
    if (ClassWin = "WorkerW")
        return
    SnapActiveWindow("top", "right", "half")
    return
}

# Original post
This is an interesting script.
I tried to convert it to v2. But some functions not working because of the following line:
NumPut('UPtr', 40, monitorInfo)

Functions which trigger with LWin+Numpad-keys and LWin+MButton are not working
Yes, I 've changed F12 to LWin for more convenience

Appreciate your help to fix this.
Last edited by afshindavoudy on 27 Feb 2024, 04:37, edited 1 time in total.
iPhilip
Posts: 823
Joined: 02 Oct 2013, 12:21

Re: Simple Windows Drag - Easily snap, move and resize windows

26 Feb 2024, 14:07

afshindavoudy wrote:
22 Feb 2024, 15:14
Appreciate your help to fix this.
Below is a fix for the GetMonitorIndexFromWindow function:

Code: Select all

GetMonitorIndexFromWindow(windowHandle) {
    ; Starts with 1.
    monitorIndex := 1

    monitorInfo := Buffer(40)
    NumPut('UInt', 40, monitorInfo)
    

    if (monitorHandle := DllCall("MonitorFromWindow", "Ptr", windowHandle, "UInt", 0x2))
        && DllCall("GetMonitorInfo", "Ptr", monitorHandle, "Ptr", monitorInfo) {
            monitorLeft := NumGet(monitorInfo, 4, "Int")
            monitorTop := NumGet(monitorInfo, 8, "Int")
            monitorRight := NumGet(monitorInfo, 12, "Int")
            monitorBottom := NumGet(monitorInfo, 16, "Int")
            workLeft := NumGet(monitorInfo, 20, "Int")
            workTop := NumGet(monitorInfo, 24, "Int")
            workRight := NumGet(monitorInfo, 28, "Int")
            workBottom := NumGet(monitorInfo, 32, "Int")
            isPrimary := NumGet(monitorInfo, 36, "UInt") & 1

            monitorCount := MonitorGetCount()

            Loop monitorCount {
                MonitorGet(A_Index, &tempMonLeft, &tempMonTop, &tempMonRight, &tempMonBottom)

                ; Compare location to determine the monitor index.
                if ((monitorLeft = tempMonLeft) and (monitorTop = tempMonTop)
                    and (monitorRight = tempMonRight) and (monitorBottom = tempMonBottom)) {
                        monitorIndex := A_Index
                        break
                }
            }
    }

    return monitorIndex
}
I hope this helps.
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
afshindavoudy
Posts: 44
Joined: 10 Jan 2024, 13:25

Re: Simple Windows Drag - Easily snap, move and resize windows

27 Feb 2024, 04:30

@iPhilip
Works perfectly
Thank you for your help
iPhilip
Posts: 823
Joined: 02 Oct 2013, 12:21

Re: Simple Windows Drag - Easily snap, move and resize windows

27 Feb 2024, 11:11

You are welcome! :)
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: zabbn and 70 guests