Jump to content

GUICtrlSetOnEventEx - MouseClick, MouseEnter, MouseLeave, MouseDown, MouseUp


FaridAgl
 Share

Recommended Posts

Here is what I came up with, it supports MouseClick, MouseEnter, MouseLeave, MouseDown and MouseUp events.

I tried to implement this in a clean way, in both coding and its logic so I followed how C#.net handles this events for a control and tried to simulate them (Guess Microsoft knows better than me!).

It has an external function which you can use and a single internal function which you shouldn't call. Also there is only 1 global variable, an array which holds everything about the registred controls and their events.

It could be used with GUIOnEventMode 1 and 0, and no matter if you switch GUIOnEventMode's value during the runtime.

Let me know how is it going for you. Comments for improvements and suggestions are greatly welcomed.

#include-once
#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

Global $GUICtrlSetOnEventEx_ahEvents[1][1]

Func GUICtrlSetOnEventEx($iCtrlId, $MouseClick = Null, $MouseEnter = Null, $MouseLeave = Null, $MouseDown = Null, $MouseUp = Null)
    ;
    ;Validate the control identifier
    ;
    Local $hCtrl = GUICtrlGetHandle($iCtrlId)
    If ($hCtrl = 0) Then
        Return False
    EndIf

    ;
    ;Validate the event functions, they must be a function, or Null
    ;
    If ((Not IsFunc($MouseClick)) And ($MouseClick <> Null)) Then
        Return False
    ElseIf ((Not IsFunc($MouseEnter)) And ($MouseEnter <> Null)) Then
        Return False
    ElseIf ((Not IsFunc($MouseLeave)) And ($MouseLeave <> Null)) Then
        Return False
    ElseIf ((Not IsFunc($MouseDown)) And ($MouseDown <> Null)) Then
        Return False
    ElseIf ((Not IsFunc($MouseUp)) And ($MouseUp <> Null)) Then
        Return False
    EndIf

    ;
    ;Delete $hCtrl and its events
    ;
    If (@NumParams = 1) Then
        ;
        ;There is no event to delete
        ;
        If ($GUICtrlSetOnEventEx_ahEvents[0][0] = 0) Then
            Return False
        EndIf

        Local $ahEvents[1][1]
        For $i = 1 To $GUICtrlSetOnEventEx_ahEvents[0][0]
            ;
            ;Skip $hCtrl as we don't want its events be listened any more
            ;
            If ($GUICtrlSetOnEventEx_ahEvents[$i][0] = $hCtrl) Then
                ContinueLoop
            EndIf

            $ahEvents[0][0] += 1
            ReDim $ahEvents[$ahEvents[0][0] + 1][6]

            ;
            ;Control identifier
            ;
            $ahEvents[$ahEvents[0][0]][0] = $GUICtrlSetOnEventEx_ahEvents[$i][0]

            ;
            ;MouseClick event
            ;
            $ahEvents[$ahEvents[0][0]][1] = $GUICtrlSetOnEventEx_ahEvents[$i][1]

            ;
            ;MouseEnter event
            ;
            $ahEvents[$ahEvents[0][0]][2] = $GUICtrlSetOnEventEx_ahEvents[$i][2]

            ;
            ;MouseDown event
            ;
            $ahEvents[$ahEvents[0][0]][3] = $GUICtrlSetOnEventEx_ahEvents[$i][3]

            ;
            ;MouseUp event
            ;
            $ahEvents[$ahEvents[0][0]][4] = $GUICtrlSetOnEventEx_ahEvents[$i][4]

            ;
            ;MouseLeave event
            ;
            $ahEvents[$ahEvents[0][0]][5] = $GUICtrlSetOnEventEx_ahEvents[$i][5]
        Next

        ;
        ;If no matches found for $hCtrl
        ;
        If ($GUICtrlSetOnEventEx_ahEvents[0][0] = $ahEvents[0][0]) Then
            Return False
        EndIf

        ;
        ;Uninitialize the event listener if $hCtrl was the last one
        ;
        If ($ahEvents[0][0] = 0) Then
            AdlibUnRegister(GUICtrlSetOnEventEx_Proc)

            $GUICtrlSetOnEventEx_ahEvents = 0
            Dim $GUICtrlSetOnEventEx_ahEvents[1][1]
        Else
            $GUICtrlSetOnEventEx_ahEvents = $ahEvents
        EndIf

        Return True
    EndIf

    ;
    ;Initialize the event listener if it's not already initialized
    ;
    If ($GUICtrlSetOnEventEx_ahEvents[0][0] = 0) Then
        If (AdlibRegister(GUICtrlSetOnEventEx_Proc, 10) = 0) Then
            Return False
        EndIf
    EndIf

    ;
    ;Update $hCtrl's events if it already exists in the event listener
    ;
    For $i = 1 To $GUICtrlSetOnEventEx_ahEvents[0][0]
        If ($GUICtrlSetOnEventEx_ahEvents[$i][0] = $hCtrl) Then
            $GUICtrlSetOnEventEx_ahEvents[$i][1] = $MouseClick
            $GUICtrlSetOnEventEx_ahEvents[$i][2] = $MouseEnter
            $GUICtrlSetOnEventEx_ahEvents[$i][3] = $MouseLeave
            $GUICtrlSetOnEventEx_ahEvents[$i][4] = $MouseDown
            $GUICtrlSetOnEventEx_ahEvents[$i][5] = $MouseUp

            Return True
        EndIf
    Next

    ;
    ;Add $hCtrl and its events to the event listener
    ;
    $GUICtrlSetOnEventEx_ahEvents[0][0] += 1
    ReDim $GUICtrlSetOnEventEx_ahEvents[$GUICtrlSetOnEventEx_ahEvents[0][0] + 1][6]

    $GUICtrlSetOnEventEx_ahEvents[$GUICtrlSetOnEventEx_ahEvents[0][0]][0] = $hCtrl
    $GUICtrlSetOnEventEx_ahEvents[$GUICtrlSetOnEventEx_ahEvents[0][0]][1] = $MouseClick
    $GUICtrlSetOnEventEx_ahEvents[$GUICtrlSetOnEventEx_ahEvents[0][0]][2] = $MouseEnter
    $GUICtrlSetOnEventEx_ahEvents[$GUICtrlSetOnEventEx_ahEvents[0][0]][3] = $MouseLeave
    $GUICtrlSetOnEventEx_ahEvents[$GUICtrlSetOnEventEx_ahEvents[0][0]][4] = $MouseDown
    $GUICtrlSetOnEventEx_ahEvents[$GUICtrlSetOnEventEx_ahEvents[0][0]][5] = $MouseUp

    Return True
EndFunc

Func GUICtrlSetOnEventEx_Proc()
    ;
    ;These variables "must" be defined as static as we need their values to be available in the next calls
    ;
    Local Static $WasPrimaryButtonDown = False
    Local Static $LastHoveredCtrlId = 0
    Local Static $LastHoveredCtrl = 0x0
    Local Static $LastPressedCtrl = 0x0
    Local Static $LastMouseUpFunction = Null
    Local Static $LastMouseLeaveFunction = Null

    ;
    ;Nonzero if the meanings of the left and right mouse buttons are swapped
    ;
    Local $AreMouseButtonsSwapped = DllCall("user32.dll", "int", "GetSystemMetrics", _
            "int", 23)[0] ;SM_SWAPBUTTON

    ;
    ;Define the primary mouse button's value
    ;
    Local $PrimaryMouseButton = (($AreMouseButtonsSwapped = 0) ? (0x01) : (0x02)) ;VK_LBUTTON : VK_RBUTTON

    ;
    ;"<> 0" is not really necessary, but let it remain there, trust me
    ;
    Local $IsPrimaryButtonDown = (DllCall("user32.dll", "short", "GetAsyncKeyState", _
            "int", $PrimaryMouseButton)[0] <> 0)

    ;
    ;Retrieves the position of the mouse cursor, in screen coordinates
    ;
    Local $tPOINT = DllStructCreate("long x;long y")
    DllCall("user32.dll", "BOOL", "GetCursorPos", _
            "ptr", DllStructGetPtr($tPOINT))

    ;
    ;Retrieves a handle to the window that contains the specified point
    ;
    Local $tCAST = DllStructCreate("INT64", DllStructGetPtr($tPOINT))
    Local $hCtrl = DllCall("user32.dll", "HWND", "WindowFromPoint", _
            "INT64", DllStructGetData($tCAST, 1))[0]

    ;
    ;Convert the control handle to control identifier
    ;
    Local $iCtrlId = DllCall("user32.dll", "int", "GetDlgCtrlID", _
            "HWND", $hCtrl)[0]

    ;
    ;Wait while the primary mouse button is pressed and the cursor is not on the pressed control
    ;
    If (($hCtrl <> $LastPressedCtrl) And ($IsPrimaryButtonDown) And ($WasPrimaryButtonDown)) Then
        Return
    EndIf

    ;
    ;Return if none of the $hCtrl and $IsPrimaryButtonDown are changed
    ;
    If (($hCtrl = $LastHoveredCtrl) And ($IsPrimaryButtonDown = $WasPrimaryButtonDown)) Then
        Return
    EndIf

    ;
    ;Primary mouse button is pressed on a new control
    ;
    If (($IsPrimaryButtonDown <> $WasPrimaryButtonDown) And ($IsPrimaryButtonDown)) Then
        $LastPressedCtrl = $hCtrl
    EndIf

    ;
    ;If the current hovered control is not the previous hovered control
    ;
    If ($hCtrl <> $LastHoveredCtrl) Then
        ;
        ;Trigger previous pressed control's MouseUp event if it has one and it's not hovered anymore
        ;
        If (($LastMouseUpFunction <> Null) And (Not $IsPrimaryButtonDown) And ($WasPrimaryButtonDown) And ($LastHoveredCtrl <> 0)) Then
            $LastMouseUpFunction($LastHoveredCtrlId)
            $LastMouseUpFunction = Null
        EndIf

        ;
        ;Trigger previous hovered control's MouseLeave event if it has one
        ;
        If ($LastMouseLeaveFunction <> Null) Then
            $LastMouseLeaveFunction($LastHoveredCtrlId)
            $LastMouseLeaveFunction = Null
        EndIf
    EndIf

    ;
    ;Is there any event registerd for $hCtrl? Let's see!
    ;
    For $i = 1 To $GUICtrlSetOnEventEx_ahEvents[0][0]
        If ($GUICtrlSetOnEventEx_ahEvents[$i][0] = $hCtrl) Then
            ;
            ;If the primary mouse button is and was pressed while the cursor was on the current hovered control
            ;
            If (($IsPrimaryButtonDown) And ($hCtrl = $LastPressedCtrl)) Then
                ;
                ;Trigger MouseDown event if it has one
                ;
                If ($GUICtrlSetOnEventEx_ahEvents[$i][4] <> Null) Then $GUICtrlSetOnEventEx_ahEvents[$i][4] ($iCtrlId)
            Else
                ;
                ;If a new control is hovered and/or the primary mouse button state has changed
                ;
                If (($hCtrl <> $LastHoveredCtrl) Or ($IsPrimaryButtonDown <> $WasPrimaryButtonDown)) Then
                    ;
                    ;Current hovered control is the previous pressed control and the primary mouse button is not pressed now, but it was
                    ;
                    If (($hCtrl = $LastPressedCtrl) And (Not $IsPrimaryButtonDown) And ($WasPrimaryButtonDown)) Then
                        ;
                        ;Trigger MouseClick event if it has one
                        ;
                        If ($GUICtrlSetOnEventEx_ahEvents[$i][1] <> Null) Then $GUICtrlSetOnEventEx_ahEvents[$i][1] ($iCtrlId)
                        ;
                        ;Trigger MouseUp event if it has one
                        ;
                        If ($GUICtrlSetOnEventEx_ahEvents[$i][5] <> Null) Then $GUICtrlSetOnEventEx_ahEvents[$i][5] ($iCtrlId)
                    ElseIf ($hCtrl <> $LastHoveredCtrl) Then
                        ;
                        ;Trigger MouseEnter event if it has one
                        ;
                        If ($GUICtrlSetOnEventEx_ahEvents[$i][2] <> Null) Then $GUICtrlSetOnEventEx_ahEvents[$i][2] ($iCtrlId)
                    EndIf
                EndIf
            EndIf

            $LastMouseUpFunction = $GUICtrlSetOnEventEx_ahEvents[$i][5]
            $LastMouseLeaveFunction = $GUICtrlSetOnEventEx_ahEvents[$i][3]

            ExitLoop
        EndIf
    Next

    $WasPrimaryButtonDown = $IsPrimaryButtonDown
    $LastHoveredCtrlId = $iCtrlId
    $LastHoveredCtrl = $hCtrl
EndFunc

Example:

#include <StaticConstants.au3>
#include <GUIConstantsEx.au3>
#include "GUICtrlSetOnEventEx.au3"

Global $hWnd = GUICreate("GUICtrlSetOnEventEx Example", 600, 400)

GUICtrlCreateButton("Button!", 25, 25, 100, 25)
GUICtrlSetOnEventEx(-1, btnMouseClick, btnMouseEnter, btnMouseLeave, btnMouseDown, btnMouseUp)

GUICtrlCreateLabel("LABEL", 25, 75, 100, 20, BitOR($SS_CENTER, $SS_CENTERIMAGE))
GUICtrlSetColor(-1, 0x1E1E1E)
GUICtrlSetOnEventEx(-1, Null, lblMouseEnter, lblMouseLeave, lblMouseDown, lblMouseUp)

Global $chkA = GUICtrlCreateCheckbox("Checkbox A", 25, 120, -1, 15)
GUICtrlSetOnEventEx(-1, Null, chkMouseEnter)

Global $chkB = GUICtrlCreateCheckbox("Checkbox B", 25, 140, -1, 15)
GUICtrlSetOnEventEx(-1, Null, chkMouseEnter)

GUISetState()

Do
Until (GUIGetMsg() = $GUI_EVENT_CLOSE)

Func btnMouseClick($iCtrlId)
    ConsoleWrite("Button clicked..." & @CRLF)
EndFunc

Func btnMouseEnter($iCtrlId)
    GUICtrlSetData($iCtrlId, "Enter")
EndFunc

Func btnMouseLeave($iCtrlId)
    GUICtrlSetData($iCtrlId, "Leave")
EndFunc

Func btnMouseDown($iCtrlId)
    GUICtrlSetData($iCtrlId, "Down")
EndFunc

Func btnMouseUp($iCtrlId)
    GUICtrlSetData($iCtrlId, "Button!")
EndFunc

Func lblMouseEnter($iCtrlId)
    GUICtrlSetBkColor($iCtrlId, 0xFEFEFE)
EndFunc

Func lblMouseLeave($iCtrlId)
    GUICtrlSetBkColor($iCtrlId, 0xEFEFF2)
EndFunc

Func lblMouseDown($iCtrlId)
    GUICtrlSetData($iCtrlId, "DOWN")
EndFunc

Func lblMouseUp($iCtrlId)
    GUICtrlSetData($iCtrlId, "LABEL")
EndFunc

Func chkMouseEnter($iCtrlId)
    Switch ($iCtrlId)
        Case $chkA
            ConsoleWrite("Checkbox A hovered...!" & @CRLF)

        case $chkB
            ConsoleWrite("Checkbox B hovered...!" & @CRLF)

    EndSwitch
EndFunc
Edited by D4RKON3
Link to comment
Share on other sites

Oh sorry, now I get what you meant.

Well, having a lots of registered controls won't cause any problem. As they suppose to be registered only once, the resize would happen once per each event registration.

But yes, if you try to frequently register/unregister hundreds of controls during the runtime, it could cause some temporary performance issue, but why should you really do that?

Thanks.

Link to comment
Share on other sites

The example is a custom listview, made with labels.

Those items are clickable so they need to be registered. And more the rows you have, more you need to register.

But as it's a easy to edit your UDF, don't change anything :)

Edited by FireFox
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...