GUI - do something when dragging a button

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
hpta
Posts: 24
Joined: 27 Sep 2019, 01:43

GUI - do something when dragging a button

27 Jul 2021, 06:53

I want this short code to get working.

Code: Select all

G := Gui()
button := G.Add("Button", "vbutton", "Drag me somewhere") 
button.OnCommand(0x0201, LButtonDown) ; invalid callback function

LButtonDown() {
   msgbox "LButtonDown"
   ; waiting here for LButtonUp 
   ; then getMousePos and do something
}

G.Show()
I know you can monitor for LButtonDown like here in the first example https://lexikos.github.io/v2/docs/commands/OnMessage.htm#Examples
But I think the way I'm trying to achieve my goal would be more elegant and I guess should be possible.
Please could you tell me whether it's just wrong or just some minor adjustment is needed?

- something similar to button.OnEvent("Click", LButtonDown) - but I want the callback to happen when LButton is down, not later when is up.
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: GUI - do something when dragging a button

27 Jul 2021, 11:08

Your callback needs to accept one parameter or define it with an asterisk in the parameter list.
hpta
Posts: 24
Joined: 27 Sep 2019, 01:43

Re: GUI - do something when dragging a button

27 Jul 2021, 13:58

You're right. There's no error if LButtonDown is has a header like this

Code: Select all

LButtonDown(*) {
But neither the message box in it fires up if the button is pressed.

What I tried to do is this:

Code: Select all

G := Gui()
button := G.Add("Button", "vbutton", "Drag me somewhere") 
button.OnCommand(0x0201, LButtonDown)
 ; documentation says OnCommand registers a function or method to be called
 ; when a control notification is received via the WM_COMMAND message.

OnMessage 0x0201, WM_LBUTTONDOWN   ; 
WM_LBUTTONDOWN(wParam, lParam, msg, hwnd)
{
   thisGuiControl := GuiCtrlFromHwnd(hwnd)
   if thisGuiControl {                 
      PostMessage 0x0111, 0x0201 , , hwnd ; so trying to send WM_COMMAND message to the button
      ; I can call my LButtonDown function from here, but I am curious about .OnCommand()
   }
}

LButtonDown(*) {
   msgbox "LButtonDown function fires up"  ; well no
   ; waiting here for LButtonUp 
   ; then getMousePos and do something
}

G.Show()
Apparently OnCommand deals with WM_COMMAND messages, so I tried to send WM_COMMAND message to the button on the line 12. Nothing is happening. Any suggestions?
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: GUI - do something when dragging a button

27 Jul 2021, 14:30

Try button.OnEvent("Click",LButtonDown) instead of OnCommand.

EDIT:

Also You try to register 0x0201 with .OnCommand() and with OnMessage(). This is going to cause problems most likely.
hpta
Posts: 24
Joined: 27 Sep 2019, 01:43

Re: GUI - do something when dragging a button

27 Jul 2021, 14:55

Try button.OnEvent("Click",LButtonDown) instead of OnCommand.
This works but the LButtonDown is called later, after LButton goes up - which is not what I want.

To explain my goal:
Gui with buttons I can "drag and drop" (visual effect of dragging doesn't necessarily have to be there) over a window (mostly Putty and one other terminal), which would trigger some function that would deal with that particular window.
I want to find out what would be the best way to approach this.
(and the ultimate goal would be queueing up these actions in one go. And to have the queue visually represented somehow :-)). Usually I use the clipboard in terminal macros quite a lot, so the actions cannot be run in parallel)
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: GUI - do something when dragging a button

27 Jul 2021, 15:12

In that case, register your events with a single method (OnCommand, or OnEvent, or OnMessage, don't double register any events) and all events should trigger the same callback. So you'll want to stick with OnMessage. If you need to capture multiple events then you can try registering WM_COMMAND (0x111), which will funnel ALL WM events to the specified callback. You can do the same for WM_NOTIFY (0x4E) for all WM_NOTIFY events. But this can get very tricky.

You'll need to read up on how to handle WM_COMMAND and WM_NOTIFY:

https://docs.microsoft.com/en-us/windows/win32/menurc/wm-command
https://docs.microsoft.com/en-us/windows/win32/controls/wm-notify

You will need to split up the hiword and the loword of wParam when using WM_COMMAND to get the info on what event is being fired.

If you use WM_NOTIFY, you need to parse the NMHDR structure in lParam to figure out which event is being fired.

Are you trying to do a "real drag and drop" ? Or are you just trying to capture individual events and try to "simulate" drag and drop?
hpta
Posts: 24
Joined: 27 Sep 2019, 01:43

Re: GUI - do something when dragging a button

27 Jul 2021, 16:47

Thank you, TheArkive.
Well, then I'll probably stick with global OnMessage and do it like this. I think it's a usable approach.

Code: Select all

G := Gui()
button := G.Add("Button", "vbutton", "Drag me somewhere") 

OnMessage 0x0201, WM_LBUTTONDOWN, -1   ; 
WM_LBUTTONDOWN(wParam, lParam, msg, hwnd)
{
   X := lParam & 0xFFFF
   Y := lParam >> 16
   thisGuiControl := GuiCtrlFromHwnd(hwnd)
   if thisGuiControl {
      if thisGuiControl.Name = "button"
         startDrag(X, Y, thisGuiControl)
   }
}

startDrag(X, Y, GuiControl) {
   ; ControlGetPos &cX, &cY, &cWidth, &cHeight, GuiControl
   ; if (X < cX) or (X > (cX + cWidth)) or (Y < cY) or (Y > (cY + cHeight)) {
   ;    return ; in case I click outside the control but it's still triggered
   ; }
   tooltip "startDrag Function"
   KeyWait "LButton"
   MouseGetPos ,, &OutputVarWin
   tooltip "I am over " WinGetTitle("ahk_id " OutputVarWin)
   sleep 1000
   tooltip
   ; do stuff based on the window and guiControl
}

G.Show()
Are you trying to do a "real drag and drop" ?
Not sure what you mean. Probably not, I just want a mouse gesture to trigger a function.

PS. I'd like to understand all the stuff with callbacks, messages, events you've mentioned. But it'll definitely take some time and effort (trying other people's code, reading documentation and forum posts)
I've been using a lot GuiControl.OnEvent, I've read the tutorial https://lexikos.github.io/v2/docs/misc/SendMessage.htm and already used program spyxx to detect windows messages.
And for example I also use a lot "copy all to clipboard" from putty with: (I found the numbers in the source code and it magically works)
SendMessage 0x0112, 0x0170, , , "ahk_pid " putty_pid
But that's basically it. If anyone knows about a good windows messaging&events tutorial for noobs, please let me know
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: GUI - do something when dragging a button

27 Jul 2021, 17:19

When I mentioned "real drag and drop" i meant registering a drop handler using COM. A bit tricky for me at this point.

If you like, just keep asking questions here. I don't know of any tutorial off hand that will get into the details of what you are trying to do.

Here's a hint that might help. With using the following reference (check the Remarks on this page):
https://docs.microsoft.com/en-us/windows/win32/menurc/wm-command

Code: Select all

WM_LBUTTONDOWN(wParam, lParam, msg, hwnd)
{
    wm_code := Format("0x{:04X}",wParam >> 16)
    ctl_id := Format("0x{:04X}",wParam & 0xFFFF)
    ; use a debugger to look at these, if you need help with that just ask!
}

Using a msgbox to try and display this kind of data would be a bad idea, since so much of it will fly by fast.

I may have the wm_code and ctl_id reversed, but it's still the same concept. So then...

Code: Select all

oops... look 3 comments ahead

It looks to me like you are on the right track. I hope you are able to take the next few steps. Those are the hardest, but then you will be able to do much more fancy stuff, and/or you'll find a better way to use .OnEvent() or OnNotify() or OnCommand() methods.

When it comes to handling lParam, you will want to do that in each IF statement, not at the top of the callback, because not every event will return an X/Y value, so this will more or less be a performance thing to consider.
Last edited by TheArkive on 27 Jul 2021, 17:49, edited 2 times in total.
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: GUI - do something when dragging a button

27 Jul 2021, 17:22

Just keep in mind that every callback event will likely be handled differently. You'll have to take the time to analyze each event to figure out if you can group them together with IF/ELSE statements (like for extracting an X/Y value or something).
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: GUI - do something when dragging a button

27 Jul 2021, 17:26

If you need help setting up a debugger, try this:

https://docs.microsoft.com/en-us/sysinternals/downloads/debugview

And use this func, copy/paste it into your script:

Code: Select all

dbg("this message will appear in the debugger") ; <-- just an example

dbg(_in) { ; the func
    Loop Parse _in, "`n", "`r"
        OutputDebug "AHK: " A_LoopField
}
Then in the debug program, click Edit > Filter/Highlight...

In the "Include" section add AHK: *. That should be it. Just leave the debugger running and watch all the messages fly by. You can also parse lParam to put whatever is in there into the debug messages too.
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: GUI - do something when dragging a button

27 Jul 2021, 17:47

Correction to 3 comments ago:

Code: Select all

OnMessage 0x111, MyCallback, -1 ; WM_COMMAND - no need for anything else, unless you want to register WM_NOTIFY (0x4E)

MyCallback(wParam, lParam, msg, hwnd)
{
    wm_code := Format("0x{:04X}",wParam >> 16)
    ctl_id := Format("0x{:04X}",wParam & 0xFFFF)

    If (wm_code = 0x201) ; WM_LBUTTONDOWN
    {
        ; do stuff
    }
    Else If (wm_code = something_else)
    {
        ; do something else
    }
}


I can show you how to parse an NMHDR structure if you like, but i suggestion you mess around with OnMessage(0x111) first. Doing this with WM_NOTIFY is almost identical, except your lParam will be much more complex than just X/Y

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: Albireo, CraigM, Draken, mikeyww and 88 guests