PopUpMenuFix Fixes the Default Menu Option Not Working In Pop Up Menu

Post your working scripts, libraries and tools for AHK v1.1 and older
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

PopUpMenuFix Fixes the Default Menu Option Not Working In Pop Up Menu

11 Sep 2017, 20:17

Initial Thread Showing the problem
https://autohotkey.com/boards/viewtopic.php?f=5&t=36903

Credit Goes To teadrinker (https://autohotkey.com/boards/memberlis ... le&u=62433)

Conversion to Singleton Static Class by me.

The Problem this solves is pressing ENTER after having Popped Up a menu with Menu,MenuName,Show. The default behaviour in windows is the Menu Shows, You hit ENTER without doing anything and the DEFAULT menu item is triggered. If you hit Escape, nothing is triggered. If you manually move with the arrows and hit enter, that selected item triggers.

This resolves the documented limitation mentioned in the manual
From the documentation on Menu:
Default [, MenuItemName]: Changes the menu's default item to be MenuItemName and makes that item's font bold (setting a default item in menus other than TRAY is currently purely cosmetic).

Code: Select all

;PopUpMenuFix.ahk
;include this file and use the static class included,  
;Fixes the Default Menu Item not working hitting enter on a menu in using Menu,MyMenu,Show
;  Where the Default Menu should be triggered
;
;PopUpMenuFix, Singleton Static Class to wrap the functionality
;   ShowPopUp(MenuName as String, X as optional coord, Y as optional Coord) 
;        The function theat replaces  Menu,,Show
;           ;Menu,MyMenu,Show
;           PopUpMenuFix.ShowPopUp("MyMenu")
;
; Credit Goes To teadrinker (https://autohotkey.com/boards/memberlist.php?mode=viewprofile&u=62433)
; Based on his code submitted in this Topic (https://autohotkey.com/boards/viewtopic.php?f=5&t=36903)
;  
; Conversion to Singleton Static Class: icuurd12b42 (https://autohotkey.com/boards/memberlist.php?mode=viewprofile&u=71341)
; Topic: https://autohotkey.com/boards/viewtopic.php?f=6&t=36947
;
;see example use below.


;uncomment below to test on it's own, proper setup demonstracted here
/*
MsgBox , PopUpMenuFix Version 1, Hit F10
; 1) Make a menu, with a Default
Loop 4
{
   Menu, MyMenu, Add, Item %A_Index%, handling
}   
;set default it item 4
Menu, MyMenu, Default, Item 3
;Add a Quit menu
Menu, MyMenu, Add, Quit, OnQuit

return

; 2) Show the menu by "Name" using the helper function, Enter should return the default when in the menu
$F10::
PopUpMenuFix.ShowPopUp("MyMenu")
*/ 
;uncomment above to test on it's own, proper setup demonstracted here
Class PopUpMenuFix
{
    static m_MenuInfo :=
    static m_hHookKeybd :=
    ;the show menu
    ShowPopUp(strMenuName, X:=-100000, Y:=-100000)
    {
        PopUpMenuFix._SetHook()
        PopUpMenuFix.m_MenuInfo.hMenu := MenuGetHandle(strMenuName)
        if(X=-100000 and Y = -100000)        {
            Menu, %strMenuName%, Show
        }
        else if(X=-100000)
        {
            Menu, %strMenuName%, Show, , %Y%
        }
        else if(Y=-100000)
        {
            Menu, %strMenuName%, Show, %X%
        }
        else
        {
            Menu, %strMenuName%, Show, %X%, %Y%
        }
        PopUpMenuFix.m_MenuInfo.hMenu := ""
        PopUpMenuFix._ReleaseHook()
    }
    ;the supporting Show Menu functions
    _SetHook()
    {
        PopUpMenuFix.m_MenuInfo := {}
        
        ObjPtr := Object(PopUpMenuFix.m_MenuInfo)
        PopUpMenuFix.m_hHookKeybd := DllCall("SetWindowsHookEx"
        , Int, WH_KEYBOARD_LL := 13
        , Ptr, RegisterCallback("PopUpMenuFix_LowLevelKeyboardProc", "", 3, ObjPtr)
        , Ptr, DllCall("GetModuleHandle", UInt, 0, Ptr)
        , UInt, 0, Ptr)
        ObjRelease(ObjPtr)
    }
    _ReleaseHook()
    {
        DllCall("UnhookWindowsHookEx", Ptr, PopUpMenuFix.m_hHookKeybd)
    }
    
}


;the calbacks
PopUpMenuFix_LowLevelKeyboardProc(nCode, wParam, lParam)
{
   static PID := DllCall("GetCurrentProcessId"), LLKHF_INJECTED := 0x10, SC_ENTER := 0x1C
   
   msg   := wParam   
   flags := NumGet(lParam + 8, "UInt")
   ext   := flags & 1
   sc    := NumGet(lParam + 4, "UInt") | ext << 8
   INJECTED := (flags & LLKHF_INJECTED) >> 4
   MenuInfo := Object(A_EventInfo)
   
   if ( sc = SC_ENTER && !INJECTED && !(msg & 1) && (hwnd := WinExist("ahk_class #32768 ahk_pid " . PID)) && hMenu := MenuInfo.hMenu )  {
      timerId := DllCall("SetTimer", Ptr, 0, Ptr, 0, UInt, 10, Ptr, RegisterCallback("PopUpMenuFix_SendKeys", "", 0, A_EventInfo), Ptr)
      MenuInfo.hWnd := hwnd, MenuInfo.TimerID := timerId
      Return 1
   }
   Return DllCall("CallNextHookEx", Ptr, 0, Int, nCode, Ptr, wParam, Ptr, lParam)
}


PopUpMenuFix_SendKeys()  {
   static SC_ENTER := 0x1C, VK_ENTER := 0xD, KEYEVENTF_KEYUP := 2
        , MF_BYPOSITION := 0x400, MF_HILITE := 0x80, MIIM_ID := 0x2
        , WM_CANCELMODE := 0x1F, WM_COMMAND := 0x111
   DefaultItem :=-1     
   MenuInfo := Object(A_EventInfo)
   DllCall("KillTimer", Ptr, 0, Ptr, MenuInfo.timerID)
   hMenu := MenuInfo.hMenu
   
   Loop % DllCall("GetMenuItemCount", Ptr, hMenu)  {
      state := DllCall("GetMenuState", Ptr, hMenu, UInt, A_Index - 1, UInt, MF_BYPOSITION)
      if (state & MF_HILITE)  {
         DefaultItem := 0
         break
      }
   }
   if (DefaultItem = 0)
      DllCall("keybd_event", UChar, VK_ENTER, UChar, SC_ENTER, UInt, 0, Ptr, 0)
   if (DefaultItem != 0)  {
      DefaultItem := DllCall("GetMenuDefaultItem", Ptr, hMenu, UInt, true, UInt, 0)
      if (DefaultItem != -1)  {
         VarSetCapacity(MENUITEMINFO, size := 4*4 + A_PtrSize*8, 0)
         NumPut(size, MENUITEMINFO)
         NumPut(MIIM_ID, MENUITEMINFO, 4)
         DllCall("GetMenuItemInfo", Ptr, hMenu, UInt, DefaultItem, UInt, true, Ptr, &MENUITEMINFO)
         MenuID := NumGet(MENUITEMINFO, 16, "UInt")
         DllCall("PostMessage", Ptr, A_ScriptHwnd, UInt, WM_CANCELMODE, Ptr, 0, Ptr, 0)
         DllCall("PostMessage", Ptr, A_ScriptHwnd, UInt, WM_COMMAND, Ptr, MenuID, Ptr, 0)
      }
   }
}
Removed the Init() and Cleanup() routines, ShowMenu() does it all
Last edited by icuurd12b42 on 16 Sep 2017, 17:14, edited 2 times in total.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: PopUpMenuFix Fixes the Default Menu Option Not Working In Pop Up Menu

13 Sep 2017, 16:12

It seems to work well, nice job :thumbup:
Minor comments, I'd be careful to use the fast option for registercallback in shared code. Also, registercallback accepts function references, so you can have the callback functions as methods in the class if you like. Remember that methods have a hidden parameter, this.
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: PopUpMenuFix Fixes the Default Menu Option Not Working In Pop Up Menu

16 Sep 2017, 00:17

Still getting to know the language so I may be doing some needless work around, please post any fix and that may improve things!
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: PopUpMenuFix Fixes the Default Menu Option Not Working In Pop Up Menu

16 Sep 2017, 16:26

Using the fast (or "f") option for the hook callback means that everytime you press a key on the keyboard you are setting ErrorLevel to 0, due to dllcalling the next hook. I think it is important to remove it, you will not notice any difference. I'd remove it for the timer to, although I didn't ponder its actual consequences. Further, for performance, you might want to just turn on the hook before showing the menu, and then turning it off when the menu is gone. There is no point in having it on when there is no menu, right? As for changing from functions to methods, there is no gain other than preference I guess. Also, you can omit the Object(A_EventInfo) stuff and access the object directly, via, PopUpMenuFix.m_MenuInfo, because PopUpMenuFix is super-global, as you may have noticed ;)
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: PopUpMenuFix Fixes the Default Menu Option Not Working In Pop Up Menu

16 Sep 2017, 17:14

Good, I updated the code to remove the Init() and Cleanup(). Since the menu locks the thread and runs modal I could Init(),Show(), and finally Cleanup() in the ShowMenu() and removed the fast option

>Also, registercallback accepts function references, so you can have the callback functions as methods in the class if you like
I've been searching for getting class functions reference and I found nothing that worked Can you help out? I would like to move the calback into the class. I also have another part of my system that has to register functions and I hate having a ton of global functions for that
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: PopUpMenuFix Fixes the Default Menu Option Not Working In Pop Up Menu

16 Sep 2017, 17:29

I would like to move the calback into the class
You set registerCallback(className.methodName) then either you make method methodName variadic, and get the parameters by adress, see registerCallback or you do

Code: Select all

method(param2,param3,param4,...){
	param1 := this 
	; ...
}
I wouldn't call any of these options ideal ;) . Also, mind the paramCount for registerCallback if you still want to use A_EvenInfo, eg,

Code: Select all

method(a,b){ ; Param count = 3 due to hidden "this"
Cheers.
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: PopUpMenuFix Fixes the Default Menu Option Not Working In Pop Up Menu

16 Sep 2017, 18:28

I got very little of that... and my limited understanding yielded this...
Let's say I move the Keyboard callback to the class

Code: Select all

Class PopUpMenuFix
{
    ....
    LowLevelKeyboardProc(nCode, wParam, lParam) ;new function for callback
    {
        PopUpMenuFix_LowLevelKeyboardProc(nCode, wParam, lParam) ;call original global function for now
    }
}
...
and changed _SetHook()'s
RegisterCallback("PopUpMenuFix_LowLevelKeyboardProc", "", 3, ObjPtr) ; global_methodName version
to
RegisterCallback("PopUpMenuFix.LowLevelKeyboardProc", "", 3, ObjPtr) ; className.methodName version

It's not really working. I am missing crucial understanding here obviously.



Related... the Func() function, which is used to map a self made ahk function to JScript object with the scripting system in my project

Not to go in much detail... Snippets

Code: Select all

DoIt()
{
msgbox, Doing It!
}
Class MyFuncs
{
    DoIt2()
    {
         msgbox, Doing It Too!
    }
}
MyJSInterpreter
{
    ....
    SetupFunctions()
    {
        this.m_Js.AddObject("DoIt", Func("DoIt")) ;WORKS
        this.m_Js.AddObject("DoIt2", Func("MyFuncs.DoIt2")) ;DONT WORK
    }
}
I would like to have all My Functions encapsulated inside the class MyFuncs. I've been lurking around and found no working solution to this yet.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: PopUpMenuFix Fixes the Default Menu Option Not Working In Pop Up Menu

17 Sep 2017, 06:35

Hello. Quick answer, registerCallback first parameter:
FunctionName
A function's name, which must be enclosed in quotes if it is a literal string. This function is called automatically whenever Address is called. The function also receives the parameters that were passed to Address.

[v1.1.06+]: A function reference can be passed instead of a function name.
PopUpMenuFix.LowLevelKeyboardProc is not a function name, it is an expression which yields a function reference. So, simply omit the quotes. Similarily for func(), hence replace Func("MyFuncs.DoIt2") with only MyFuncs.DoIt2. For the callback method, working example,

Code: Select all

dllcall(registerCallback(popupmenufix.LowLevelKeyboardProc,,3,456), "int", 1, "int", 2, "int", 3) ; Note, paramcount = 3
Class PopUpMenuFix
{
    LowLevelKeyboardProc(wParam, lParam)
    {
        nCode := this
		msgbox % nCode "`n" wParam "`n" lParam "`n" A_EventInfo
    }
}
Cheers.
icuurd12b42
Posts: 202
Joined: 14 Aug 2016, 04:08

Re: PopUpMenuFix Fixes the Default Menu Option Not Working In Pop Up Menu

18 Sep 2017, 04:45

Thanks, I'll have another go at it though I think I tried it without the quotes. but Hey, I'm all over the place with my project and posting modified snippets of it over here as helper scripts... I'm likely to have done something wrong while testing

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 244 guests