AutoHotkey Community

It is currently May 27th, 2012, 11:44 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: October 16th, 2005, 12:42 am 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
1. If a custom menu is shown called by a mouse event, subsequent events should close the menu and the script should intercept the WM message. Now the script misses the message.

2. It would simplify scripts if menu names could be stored in variables and given to the Menu Show command.

In a script I defined many right-click menus, different ones for different edit boxes. If I don't select anything from a menu, but right click the same edit box again, the standard right-click menu pops up instead. Right-clicking again in the edit box pops up my custom menu, alternating between it and the standard menu. It is quite disturbing, but can be worked around with timers. Unfortunately, the Menu Show command does not accept the menu name in a variable, so for each menu in the script we need a different timer subroutine. Here is the working version.
Code:
Gui Add, Edit,r2 w200 ReadOnly vResult, ABC
Gui Show
OnMessage(0x204, "RButtonDown")     ; WM_RBUTTONDOWN
Menu ResultMenu, Add, &Select, ResultSelect
Menu ResultMenu, Add, &Copy,   ResultCopy
Return

ResultSelect:
ResultCopy:
   TrayTip,,%A_ThisMenuItem%
Return

RButtonDown()
{
   If A_Gui
      If A_GuiControl = Result
         Settimer ShowResultMenu, 10
}

ShowResultMenu:
   Settimer ShowResultMenu, Off
   Menu ResultMenu, Show
Return
The simpler version of the RButtonDown function produces the alternating right-click menus.
Code:
RButtonDown()
{
   If A_Gui
      If A_GuiControl = Result
         Menu ResultMenu, Show
}
Below is the script, which causes a run time error.
Code:
RButtonDown()
{
   If A_Gui
      If A_GuiControl = Result
      {
         Menu2show = ResultMenu
         Settimer ShowMenu, 10
      }
}

ShowMenu:
   Settimer ShowMenu, Off
   Menu %Menu2show%, Show  ; Run-time Error: Menu does not exist
Return


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 16th, 2005, 2:45 am 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
Laszlo wrote:
1. If a custom menu is shown called by a mouse event, subsequent events should close the menu and the script should intercept the WM message. Now the script misses the message.

If I don't select anything from a menu, but right click the same edit box again, the standard right-click menu pops up instead [of my custom popup menu].

I believe both of the above are caused by the following documented limitation of OnMessage: "If a monitored message that is numerically less than 0x312 arrives while the script is absolutely uninterruptible -- such as while a menu is displayed, a KeyDelay/MouseDelay is in progress, or the clipboard is being opened -- the message's function will not be called and the message will be treated as unmonitored."

However, I've lately been pondering whether the policy of forbidding hotkeys from launching while the script is displaying a menu should be changed. If it gets changed for hotkeys, it might get changed for OnMessage too.

Quote:
2. It would simplify scripts if menu names could be stored in variables and given to the Menu Show command.
It's already allowed. Just be sure to declare variables as global inside a function if some global subroutine needs to access them.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 16th, 2005, 4:18 am 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
Damn! I keep forgetting about Global!

Then this limitation about messages less than 0x312 is not that bad. We only need 10 extra lines of AHK code. Chris, maybe you could add to the documentation a workaround, something like this:
Code:
Gui Add, Edit,r2 w200 ReadOnly vResult
; add here the other GUI controls
Gui Show

Menu ResultMenu, Add, &Select, ResultSelect
Menu ResultMenu, Add, &Copy,   ResultCopy
; add here menus for the other GUI controls

OnMessage(0x204,"RButtonDown")   ; React to right-click: 0x204 = WM_RBUTTONDOWN
Return

ResultSelect:
ResultCopy:
   TrayTip,,%A_ThisMenuItem%     ; add here real code for the menu items
Return

RButtonDown()
{
   If A_GUI
      If A_GuiControl = Result
         Show("ResultMenu")
      ; add here calls for showing menus of other GUI controls
}

Show(Menu)                       ; set up timer to show menu in another thread
{
   Global
   Menu2Show = %Menu%
   Settimer ShowMenu, 10
}

ShowMenu:                        ; timer subroutine to show the menu
   Settimer ShowMenu, Off
   Menu %Menu2Show%, Show
Return


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 16th, 2005, 3:06 pm 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
Laszlo wrote:
Damn! I keep forgetting about Global!
I'm sure you're not the only one. Although the requirement for Global improves script maintainability, it's probably more draconian than it's worth. However, another reason it was done is that it simplified the code a lot.

In the future, there will probably be an ability to declare super-global variables by using the global keyword outside of any function. This would cause such variables to be global to all functions.

Quote:
Then this limitation about messages less than 0x312 is not that bad.
The explanation in my previous post was wrong. Instead, both of the behaviors are caused by the following documented limitation: "If a message arrives while its function is still running due to a previous arrival of the same message, the function will not be called again; instead, the message will be treated as unmonitored."

Since there are already some work-arounds for this described in the documentation, I probably won't add your example to it. However, in the future there may be a way to optionally allow more than one thread per OnMessage function.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 16th, 2005, 5:36 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
Chris wrote:
...in the future there may be a way to optionally allow more than one thread per OnMessage function.
I thought allowing more than one thread would open further copies of the custom menu, which is not what we want. An option would be nice, allowing subsequent occurrences of the same event to close the menu (terminate the running thread). I did read the documentation, but the suggested workarounds do not work in my system. This is why I posted originally a script. I tried adding Critical in the subroutine and Thread Interrupt,0,0 (also with many other pairs of parameters). Neither has any effect on the alternating standard/custom menus. Could anyone post a script, where the documented workarounds prevent the alternating menus?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 17th, 2005, 1:31 am 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
Laszlo wrote:
I tried adding Critical in the subroutine and Thread Interrupt,0,0 (also with many other pairs of parameters). Neither has any effect on the alternating standard/custom menus.
You're right, that's caused by the following: "A message less than 0x312 cannot be buffered by Critical or Thread Interrupt; the only way to guarantee that no such messages are missed is to ensure the function finishes in under 10 milliseconds. One way to do this is to have it queue up a future thread by posting to its own script a monitored message number higher than 0x312. That message's function should use Critical as its first line to ensure that its messages are buffered."

I think the above used to mention your SetTimer method as a workaround, but then someone mentioned the idea above as superior so I changed it. Since the documentation for OnMessage is already dauntingly long, it seems best to devote only a small amount of space to obscure issues.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 17th, 2005, 2:59 am 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
For me this is not an obscure issue. Right-click menus are very common, and popping up the wrong one every other time makes our scripts look amateurish or buggy. I try to use AHK in my work, writing small applications, but they must look professional to be accepted.

In any case, with Chris' hints, I could come up with a script which uses the documented workarounds. I post it here, so anyone can find it, who has similar problems. Having many GUI controls this technique makes the script longer, but more reliable, as the timer method (very rarely) still pops up the wrong menu.
Code:
Gui Add, Edit,r2 w200 ReadOnly vResult
; add here the other GUI controls
Gui Show

Menu ResultMenu, Add, &Select, ResultSelect
Menu ResultMenu, Add, &Copy,   ResultCopy
; add here menus for the other GUI controls

OnMessage(0x204,"RButtonDown")   ; React to right-click: 0x204 = WM_RBUTTONDOWN
OnMessage(0x3FF,"RButtonDnFF")   ; Buffered private message, Msg > 0x312
Exit

ResultSelect:
ResultCopy:
   TrayTip,,%A_ThisMenuItem%     ; add here real code for the menu items
Return

RButtonDown()
{
   If A_GUI
      IfEqual A_GuiControl,Result, PostMessage 0x3FF, 1 ; 0x3FF, 2... for others
      ; Else IfEqual A_GuiControl... add here messages for other Gui controls
}

RButtonDnFF(wParam)
{
   Critical                      ; only works with Msg > 0x312
   IfEqual wParam,1, Menu ResultMenu, Show
   ; Else IfEqual... add here tests & showing menus of other GUI controls
}


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 17th, 2005, 1:44 pm 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
Laszlo wrote:
For me this is not an obscure issue. Right-click menus are very common...
I meant no offense, only that the preferred method of doing right-click menus is GuiContextMenu. I realize that you can't use that in this case because you're overriding an Edit control's built-in context menu.

Thanks for posting your final solution.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 17th, 2005, 4:39 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
Chris wrote:
the preferred method of doing right-click menus is GuiContextMenu
I could not make it work in my system. Try the following script. As I understood the documentation, at right click in the edit box the MsgBox should pop up. It does not. Only at a right click in the empty area inside the GUI window triggers the GuiContextMenu subroutine. Double clicks are not caught anywhere, so I could not find other ways to react to mouse clicks, but the OnMessage method above. Did I do something stupid like in my original post omitting the Global directive?
Code:
Gui Add, Edit,r2 w200 vResult
Gui Show, w250
Exit
GuiContextMenu:
   MsgBox Control = %A_GuiControl%`nEvent = %A_GuiEvent%
Return


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 17th, 2005, 8:02 pm 
Offline

Joined: September 25th, 2005, 4:31 pm
Posts: 610
Below is a demonstation of another method to introduce custom context menus in AHk GUI windows. Some notable features:

    * preserves full RButton functionality in unspecified windows (i.e., contexts)
    * enables coexistance of default and custom menu. default menu is shown when RButton is depressed and released in a time less than threshold and, if depressed longer, will show the custom menu. the idea for this dual existance was demonstrated by Decarlo110.


Code:
theshold = 200

Gui, Add, Edit, x5 y5 w400 h200, hybrid context menu
Gui, Add, Edit, x5 y210 w400 h200, default context menu
Gui, Show,, test window

WinGet, hw_test, ID, test window ahk_class AutoHotkeyGUI

menu_context := false
return

GuiClose:
ExitApp

activate_custom_menu:
   SetTimer, activate_custom_menu, off
   
   if ( ! menu_context )
   {
      Menu, context, Add, menu_context_ShowHello
      
      menu_context := true
   }
   
   Menu, context, Show
return

$RButton::
   MouseGetPos,,, wid, control

   if ( wid = hw_test and control = "Edit1" )
      SetTimer, activate_custom_menu, %threshold%
   else
      Send, {RButton Down}
return

$RButton Up::
   SetTimer, activate_custom_menu, off

   MouseGetPos,,, wid, control
   
   if ( wid = hw_test and control = "Edit1" )
      Send, {RButton Down}{RButton Up}
   else
      Send, {RButton Up}
return

menu_context_ShowHello:
   MsgBox, Hello`, World!
return


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 17th, 2005, 10:57 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
Thanks, Shimanov! This approach, monitoring the state of the right mouse button and doing special actions when the cursor is over designated areas (and keys are pressed for a long time), could be made to work. I think the following script is functionally equivalent, but shorter and more robust (the original freezes if I right click on the title bar).
Code:
Gui, Add, Edit, w400 h200, hybrid context menu
Gui, Add, Edit, w400 h200, default context menu
Gui, Show, w500, test window
WinGet, hw_test, ID, test window ahk_class AutoHotkeyGUI
Menu, context, Add, menu_context_ShowHello
return

$RButton::
   MouseGetPos,,,wid, control
   if ( wid = hw_test and control = "Edit1" )
   {
      KeyWait RButton, T0.2
      If ErrorLevel  ; long press
      {
         Menu, context, Show
         Return
      }
   }
   MouseClick Right
return

menu_context_ShowHello:
   MsgBox, Hello`, World!
return
However, both suffer from the same problem: If a right-click pops up a menu (custom or standard), subsequent right-clicks anywhere in the same GUI are not registered by AHK, until another mouse button is pressed or the active window is changed. Looks like an AHK bug or something is weird with my PC. Problems like this and the one with GuiContextMenu made me switch to the OnMessage method. Could you check that the problematic cases work in your PC?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 18th, 2005, 3:25 am 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
Laszlo wrote:
As I understood the documentation, at right click in the edit box the MsgBox should pop up. It does not.
An Edit control's context menu takes precedence by design (and this is documented). You might notice earlier that I said, "I realize that you can't use [GuiContextMenu] in this case because you're overriding an Edit control's built-in context menu."


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 18th, 2005, 3:25 am 
Offline

Joined: September 25th, 2005, 4:31 pm
Posts: 610
Laszlo wrote:
following script is functionally equivalent


Not quite. It doesn't support right button drag and drop.

Quote:
shorter and more robust (the original freezes if I right click on the title bar)


I have confirmed this behavior. It is likely a bug with AHk. I have modified AHk to resolve this issue. You can try a preview of this revised version here and the revised source.

Quote:
both suffer from the same problem: If a right-click pops up a menu (custom or standard), subsequent right-clicks anywhere in the same GUI are not registered by AHK


Also confirmed. Try the following revised code:

(It seems to catch every right click [to your heart's content].)

Code:
Menu, menu_context, Add, menu_context_ShowHello
      
Gui, Add, Edit, x5 y5 w400 h200, hybrid context menu
Gui, Add, Edit, x5 y210 w400 h200, default context menu
Gui, Show,, test window

WinGet, hw_test, ID, test window ahk_class AutoHotkeyGUI

WM_RBUTTONDOWN = 0x204
OnMessage( WM_RBUTTONDOWN, "HandleMessage" )
return

GuiClose:
ExitApp

HandleMessage( p_w, p_l, p_m, p_hw )
{
   Gosub, $RButton
}

timer_ShowCustomMenu:
   SetTimer, timer_ShowCustomMenu, off
   
   Menu, menu_context, Show
return

$RButton::
   MouseGetPos,,, wid, control

   if ( wid = hw_test and control = "Edit1" )
      SetTimer, timer_ShowCustomMenu, 200
   else
      Send, {RButton Down}
return

$RButton Up::
   SetTimer, timer_ShowCustomMenu, off

   MouseGetPos,,, wid, control
   
   if ( wid = hw_test and control = "Edit1" )
      Send, {RButton Down}{RButton Up}
   else
      Send, {RButton Up}
return

menu_context_ShowHello:
   MsgBox, Hello`, World!
return


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 18th, 2005, 7:21 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
Chris wrote:
An Edit control's context menu takes precedence by design (and this is documented).
I realized that. This is why my post is not in Bug Reports, but a wish: could it be changed?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 18th, 2005, 7:53 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
Shimanov, your changes in AHK and your last script seems to work. However, my version (with right-drag) still does not. (Needs a left-click or key press before the custom menu starts working again.)
Code:
$RButton::
   MouseGetPos,,,wid, control
   if ( wid = hw_test and control = "Edit1" )
   {
      KeyWait RButton, T0.2
      If ErrorLevel  ; long press
      {
         Menu, context, Show
         Return
      }
   }
   Send {RButton Down}
return

$RButton Up::
   Send {RButton Up}
Return
Is there a problem in there?


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: Google Feedfetcher and 2 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group