MouseCursor class - no flickers - 2021/06/21 - beta.1

Post your working scripts, libraries and tools.
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

MouseCursor class - no flickers - 2021/06/21 - beta.1

Post by TheArkive » 22 Apr 2021, 11:51

Thanks to @teadrinker for his ReplaceSystemCursor() script here. That got me started.

Here is a quick and easy way to change the mouse cursor with a GUI and avoid flickering back to the arrow cursor.

This will only work on GUI windows spawned by the script (in general). If you hook into another window's events you may be able to apply this to other windows.

Code: Select all

; Thanks to teadrinker for his ReplaceSystemCursor() script which got me started.
; https://www.autohotkey.com/boards/viewtopic.php?p=388118#p388118
; I borrowed the default cursor property list.
; =====================================================================
; Example
; =====================================================================
OnMessage(0x200,mouse_move)

g := Gui("+Resize")
g.OnEvent("close",gui_close)

g.Add("Edit","vEdit1 w250 h250")
g.Add("Edit","vEdit2 x+0 w250 h250")
g.Show("h300")

gui_close(*) {
    ExitApp
}

F12::ExitApp

mouse_move(wParam,lParam,msg,hwnd) {    ; You must define how and when
    Global g                            ; want to change cursors.
    If (hwnd = g["Edit1"].hwnd)         ; WM_MOUSEMOVE is only one way, and
        MouseCursor.Set("Cross")        ; checking the hwnd is still only one
    Else If (hwnd = g["Edit2"].hwnd)    ; way.  You could also check mouse
        MouseCursor.Set("Help")         ; coords in a particular range.
    Else
        MouseCursor.Set()               ; Reset cursor back to arrow.
    
    return false
}

; =====================================================================
; MouseCursor Class - easy and flicker-free changing of mouse cursor.
;
;   MouseCursor.Set(_cursor := "")
;   * Specified _cursor can be a handle or one of the string properties
;     from the __resources member.
;   * Leave blank to reset the cursor back to the Arrow cursor.
; =====================================================================
class MouseCursor {
    Static __resources := { AppStarting:32650 ; https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setsystemcursor
                          , Arrow:32512, Cross:32515, Hand:32649, Help:32651, IBeam:32513, No:32648, SizeAll:32646
                          , SizeNESW:32643, SizeNWSE:32642, SizeWE:32644, SizeNS:32645 , UpArrow:32516, Wait:32514 }
    Static __New() {
        this.__cursors := {}, this.__allow := false
        For name, value in this.__resources.OwnProps()
            this.__cursors.%name% := DllCall("user32\LoadImage","UPtr",0,"Str","#" value,"UInt",2 ; 2 = cursor
                                            ,"Int",0,"Int",0,"UInt",0x8000) ; 0x8000 = LR_SHARED
        OnMessage(0x20,this.WM_SETCURSOR)
    }
    Static WM_SETCURSOR(*) {
        return MouseCursor.__allow
    }
    Static Set(_cursor:="") {
        If (_cursor) {
            If (Type(_cursor) = "string") And !this.__cursors.HasProp(_cursor)
                throw Error("Invalid cursor specified.",,_cursor)
            c := IsInteger(_cursor) ? _cursor : this.__cursors.%_cursor%
            this.__allow := true
            return DllCall("SetCursor","UPtr",c)
        } Else {
            this.__allow := false ; allow WM_SETCURSOR to change the cursor
            return DllCall("SetCursor","UPtr",this.__cursors.Arrow) ; "Arrow" is generally the default
        }
    }
}

===================================================
Updates
===================================================

I did notice some flickering when i put OutputDebug in the WM_SETCURSOR callback. So if you have something going on that uses more CPU, chances are you will get some flickering. But if you keep it light-weight, then you should be flicker-free.

EDIT: MouseCursor.Set() will take a standard string value for a cursor (same as the property names in __resources), or it will take a cursor handle.

One more caveat: once the mouse cursor leaves the GUI, it goes back to arrow, because other windows are throwing their own WM_SETCURSOR messages.

Return to “Scripts and Functions (v2)”