Code: Select all
#NoEnv
SetBatchLines, -1
CoordMode, ToolTip, Screen ; Required for ShowMenuCheckCursorPosition()
Menu, MyMenu, Add, Item 1, MenuHandler
Menu, MyMenu, Add, Item 2, MenuHandler
Menu, MySubMenu3, Add, Item 3.1, MenuHandler
Menu, MySubMenu3, Add, Item 3.2, MenuHandler
Menu, MySubMenu3, Add, Item 3.3, MenuHandler
Menu, MySubMenu3, Add, Item 3.4, MenuHandler
Menu, MySubMenu3, Add, Item 3.5, MenuHandler
Menu, MyMenu, Add, Item 3, :MySubMenu3
Menu, MyMenu, Add, Item 4, MenuHandler
Menu, MySubMenu5, Add, Item 5.1, MenuHandler
Menu, MySubMenu5, Add, Item 5.2, MenuHandler
Menu, MySubMenu5.3, Add, Item 5.3.1, MenuHandler
Menu, MySubMenu5.3, Add, Item 5.3.2, MenuHandler
Menu, MySubMenu5.3, Add, Item 5.3.3, MenuHandler
Menu, MySubMenu5.3, Add, Item 5.3.4, MenuHandler
Menu, MySubMenu5.3, Add, Item 5.3.5, MenuHandler
Menu, MySubMenu5, Add, Item 5.3, :MySubMenu5.3
Menu, MySubMenu5, Add, Item 5.4, MenuHandler
Menu, MySubMenu5, Add, Item 5.5, MenuHandler
Menu, MyMenu, Add, Item 5, :MySubMenu5
Gui, Show, w400 h400, Context Menu Gui
global ToolTipText := {}
AssignToolTipTexts("MyMenu")
AssignToolTipTexts("MySubMenu3")
AssignToolTipTexts("MySubMenu5")
AssignToolTipTexts("MySubMenu5.3")
Return
AssignToolTipTexts(MenuName) {
HMENU := MenuGetHandle(MenuName)
ToolTipText[HMENU] := []
Loop, % Menu_GetItemCount(HMENU)
ToolTipText[HMENU][A_Index] := MenuName " - Item " A_Index
}
GuiClose:
ExitApp
GuiContextMenu:
ShowMenu(MenuGetHandle("MyMenu"), , , 3)
Return
MenuHandler:
MsgBox, 0, %A_ThisLabel%, %A_ThisMenu% - %A_ThisMenuItem%
Menu_CheckRadioGroup(MenuGetHandle(A_ThisMenu), A_ThisMenuItemPos, 1)
Return
; ==================================================================================================================================
; Shows a user-defined menu without blocking the script.
; Parameters:
; HMENU - name or handle of a user-defined menu. This menu must not be the 'Tray' menu.
; X - x-position of the upper-left corner of the menu in screen coordinates.
; Default: -1 - current cursor position.
; Y - y-position of the upper-left corner of the menu in screen coordinates.
; Default: -1 - current cursor position.
; WhichToolTip - Tooltip window to operate upon (1-20)
; Default: 1
; Return value:
; True if the menu could be shown, otherwise False.
; Remarks:
; Parts of code adapted from script_menu.cpp -> UserMenu::Display
; Because the function prevents the default AHK handling of menues there might be unwanted side-effects.
; ==================================================================================================================================
ShowMenu(HMENU, X := -1, Y := -1, WhichToolTip := 1) {
If !DllCall("IsMenu", "Ptr", HMENU) && !(HMENU := MenuGetHandle(HMENU))
Return False
If (MenuGetName(HMENU) = "Tray")
Return False
; Determine the display position
If (X = -1) || (Y = -1) {
VarSetCapacity(PT, 8, 0)
DllCall("GetCursorPos", "Ptr", &PT)
If (X = -1)
X := NumGet(PT, 0, "Int")
If (Y = -1)
Y := NumGet(PT, 4, "Int")
}
; Set the AHK main window as the foreground window, if needed
ThisTID := DllCall("GetCurrentThreadId", "UInt")
ForeWin := DllCall("GetForegroundWindow", "UPtr")
If (ChangeFore := !(ForeWin) || (DllCall("GetWindowThreadProcessId", "Ptr", ForeWin, "Ptr", 0, "UInt") <> ThisTID))
While !DllCall("SetForegroundWindow", "Ptr", A_ScriptHwnd, "UInt")
DllCall("Sleep", "UInt", 10)
; Activate the message handler
MsgFunc := Func("ShowMenuEnterMenuLoop")
OnMessage(0x0211, MsgFunc, -1) ; WM_ENTERMENULOOP
; Activate the timer
TimerFunc := Func("ShowMenuCheckCursorPosition").Bind(HMENU, WhichToolTip)
SetTimer, %TimerFunc%, 10
Result := DllCall("TrackPopupMenuEx", "Ptr", HMENU, "UInt", 0, "Int", X, "Int", Y, "Ptr", A_ScriptHwnd, "Ptr", 0, "UInt")
; Deactivate the timer
SetTimer, %TimerFunc%, Delete
; Remove the ToolTip, if any
ToolTip, , , , WhichToolTip
; Deactivate the message handler
OnMessage(0x0211, MsgFunc, 0) ; WM_ENTERMENULOOP
; Reset the timer's static variables
ShowMenuCheckCursorPosition(0, WhichToolTip)
; Restore the foreground window, if needed
If (ChangeFore) && (ForeWin) && (DllCall("GetForegroundWindow", "UPtr") = A_ScriptHwnd)
DllCall("SetForegroundWindow", "Ptr", ForeWin, "UInt")
Return Result
}
; ==================================================================================================================================
ShowMenuEnterMenuLoop() {
Return 0 ; prevents AHK menu processing
}
; ==================================================================================================================================
ShowMenuCheckCursorPosition(HMENU, WhichToolTip) {
Static PrevItem := 0, ThisMenu := 0, Menus := []
If (HMENU = 0) {
PrevItem := 0, ThisMenu := 0, Menus := []
Return
}
If !Menus.MaxIndex()
Menus.Push(ThisMenu := HMENU)
DllCall("GetCursorPos", "Int64P", PT)
If (Item := DllCall("MenuItemFromPoint", "Ptr", 0, "Ptr", ThisMenu, "Int64", PT, "Int") + 1) {
If (Item <> PrevItem) {
Pos := Menu_GetItemRect(ThisMenu, Item)
ToolTip, % ToolTipText[ThisMenu][Item], Pos.right, (Pos.top + Pos.bottom)//2, WhichToolTip ; Requires CoordMode, ToolTip, Screen
}
}
Else If (HSUBMENU := DllCall("GetSubMenu", "Ptr", ThisMenu, "Int", PrevItem-1))
Menus.Push(ThisMenu := HSUBMENU)
Else {
If (Menus.MaxIndex() > 1 && (DllCall("MenuItemFromPoint", "Ptr", 0, "Ptr", Menus[Menus.MaxIndex()-1], "Int64", PT, "Int") + 1)) {
Menus.Pop()
ThisMenu := Menus[Menus.MaxIndex()]
}
ToolTip, , , , WhichToolTip
}
PrevItem := Item
}
; ==================================================================================================================================
; GetItemRect Retrieves the bounding rectangle for the specified menu item.
; Return values: If the function succeeds, the return value is an array containing the coordinates of the bounding rectangle of the
; specified menu item expressed in screen coordinates.
; If the function fails, the return value is null.
; ==================================================================================================================================
Menu_GetItemRect(hMenu, Item) {
VarSetCapacity(RECT, 16, 0)
if DllCall("GetMenuItemRect", "Ptr", 0, "Ptr", hMenu, "UInt", Item-1, "Ptr", &RECT)
Return {left:NumGet(RECT,0,"Int"),top:NumGet(RECT,4,"Int"),right:NumGet(RECT,8,"Int"),bottom:NumGet(RECT,12,"Int")}
}
; ==================================================================================================================================
; CheckRadioGroup Checks a specified menu item and makes it a radio item. At the same time, the function clears
; all other menu items in the associated group and clears the radio-item type flag for those items.
; If the specified menu item is the same as the previous one, the menu item is cleared.
; Parameters: First - The 1-based position of the first menu item in the group.
; Default: 0 = no radio group.
; Last - The 1-based position of the last menu item in the group.
; Default: 0 = last menu item.
; Return values: If the function succeeds, the return value is nonzero; otherwise it is zero (False).
; Note: To uncheck all items in the group specify 0 for Pos, 1 for First, and 0 for Last.
; To treat all items in the menu as a group pass 1 for First and 0 for Last.
; To check/uncheck the item inidividually use the menu command.
;
; ==================================================================================================================================
Menu_CheckRadioGroup(HMENU, Pos, First := 0, Last := 0) {
Static PrevHMENU, PrevPos
If (First < 1)
First := Last := Pos
If (Last < 1)
Last := Menu_GetItemCount(HMENU)
If (HMENU = PrevHMENU && Pos = PrevPos) ; If selecting the previous menu item, clear it and return
Return DllCall("CheckMenuRadioItem", "Ptr", HMENU, "UInt", 0, "UInt", Menu_GetItemCount(HMENU) - 1, "UInt", -1, "UInt", 0x0400, "UInt") ; MF_BYPOSITION = 0x0400
If (HMENU <> PrevHMENU) ; if the specified menu is different than the previous one, clear the all the items in the previous menu
DllCall("CheckMenuRadioItem", "Ptr", PrevHMENU, "UInt", 0, "UInt", Menu_GetItemCount(PrevHMENU) - 1, "UInt", -1, "UInt", 0x0400, "UInt") ; MF_BYPOSITION = 0x0400
PrevHMENU := HMENU, PrevPos := Pos
Return DllCall("CheckMenuRadioItem", "Ptr", HMENU, "UInt", First - 1, "UInt", Last - 1, "UInt", Pos - 1, "UInt", 0x0400, "UInt") ; MF_BYPOSITION = 0x0400
}
; ==================================================================================================================================
; GetItemCount Determines the number of items in the specified menu.
; Return values: If the function succeeds, the return value specifies the number of items in the menu.
; If the function fails, the return value is -1
; ==================================================================================================================================
Menu_GetItemCount(HMENU) {
Return DllCall("GetMenuItemCount", "Ptr", HMENU, "Int")
}
function so that it returns correct values for AutoHotkey U64.