ShowMenu() : in center screen.

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
SKAN
Posts: 1619
Joined: 29 Sep 2013, 16:58

ShowMenu() : in center screen.

Post by SKAN » 15 Sep 2020, 18:00

Intro: : Default Menu command doesn't have flags options to control the display of menu. for eg. displaying a menu at absolute center of screen.
My need: When we click on Start button, start menu appears nicely docked to the task bar. But if we right-click on the AHK notification icon, it appears
partly over the tray obstructing view of other icons. To make tray menu behave like Start menu, I need the alignment flags and hence I wrote this function.
Edit: A demo Start menu like Tray menu has been posted a few posts below.
 
 
ShowMenu(hMenu, MenuLoop, X, Y, Flags)

Parameters:
 
  • hMenu. Create a menu and use MenuGetHandle() to get a Win32 handle.
  • MenuLoop: By default, AutoHotkey block timers, non-system messages etc., when a Menu is being shown.
    Pass true if you want to bypass this. I don't recommend this option for serious/sensitive scripts.
  • X, Y. The screen position/point for Menu. If you use MouseGetPos to get X,Y then set CoordMode to screen.
  • Flags. Default value is 0 (TPM_LEFTALIGN |TPM_TOPALIGN). Combine one each from vertical/horizontal for menu alignment relative to X,Y
    • Horizontal
      TPM_CENTERALIGN 0x04
      TPM_LEFTALIGN 0x00
      TPM_RIGHTALIGN 0x08
    • Vertical
      TPM_BOTTOMALIGN 0x20
      TPM_TOPALIGN 0x00
      TPM_VCENTERALIGN 0x10
    The demo uses TPM_VCENTERALIGN | TPM_CENTERALIGN
 
 
The function: Along with a demo.
 

Code: Select all

ShowMenu(hMenu, MenuLoop:=0, X:=0, Y:=0, Flags:=0) {            ; Ver 0.61 by SKAN on D39F/D39G
Local                                                           ;            @ tiny.cc/showmenu
  If (hMenu="WM_ENTERMENULOOP")
    Return True
  Fn := Func("ShowMenu").Bind("WM_ENTERMENULOOP"), n := MenuLoop=0 ? 0 : OnMessage(0x211,Fn,-1)
  DllCall("SetForegroundWindow","Ptr",A_ScriptHwnd)     
  R := DllCall("TrackPopupMenu", "Ptr",hMenu, "Int",Flags, "Int",X, "Int",Y, "Int",0
             , "Ptr",A_ScriptHwnd, "Ptr",0, "UInt"),                     OnMessage(0x211,Fn, 0)
  DllCall("PostMessage", "Ptr",A_ScriptHwnd, "Int",0, "Ptr",0, "Ptr",0)
Return R
}

; Demo follows

#NoEnv
#SingleInstance, Force
CoordMode, Mouse, Screen
MouseMove, A_ScreenWidth/2, A_ScreenHeight/2, 0
ShowMenu( MenuGetHandle("Tray"), False, A_ScreenWidth/2, A_ScreenHeight/2, 0x14 )
Return
My Scripts and Functions: V1  V2

User avatar
SirSocks
Posts: 360
Joined: 26 Oct 2018, 08:14

Re: ShowMenu() : in center screen.

Post by SirSocks » 17 Sep 2020, 07:18

Very cool idea to pull up the menu without clicking on the icon. This could be very useful to build drop-down menu's in non-conventional locations. Such as clicking on a button and making a dropdown menu appear under the cursor. I like it! Thank you for sharing this!

User avatar
SKAN
Posts: 1619
Joined: 29 Sep 2013, 16:58

Re: ShowMenu() : in center screen.

Post by SKAN » 17 Sep 2020, 10:41

@SirSocks

Thanks for the feedback. :) :thumbup:
I'm actually using it to make traymenu look as though it is docked to the taskbar... like start menu.
 
 
A 'docked-like' menu looks like:
Spoiler
 
 
Whereas, a normal right click would show menu like:
Spoiler
My Scripts and Functions: V1  V2

User avatar
SirSocks
Posts: 360
Joined: 26 Oct 2018, 08:14

Re: ShowMenu() : in center screen.

Post by SirSocks » 17 Sep 2020, 11:09

Great screen shot examples! I'll have to give that a try.

User avatar
SKAN
Posts: 1619
Joined: 29 Sep 2013, 16:58

Start menu like Tray menu

Post by SKAN » 17 Sep 2020, 11:13

SirSocks wrote:
17 Sep 2020, 11:09
Great screen shot examples! I'll have to give that a try.
Sure. I wrote the following script to take the screen-shots :thumbup:
Run script and try left click vs right click.
 
  • Left click (nice and docked-like appearance with ShowMenu().)
    Image
     
  • RightClick (Regular show.. at cursor pos)
    Image
 

Code: Select all

#NoEnv
#SingleInstance, Force
Menu, Tray, NoStandard
Menu, Tray, Add, Menu, Routine
Menu, Tray, Default, Menu
Menu, Tray, Add
Menu, Tray, Click, 1
Menu, Tray, Standard
Return

Routine:
  ShowMenu(MenuGetHandle("Tray"), False, TrayMenuParams()*)
Return

ShowMenu(hMenu, MenuLoop:=0, X:=0, Y:=0, Flags:=0) {            ; Ver 0.61 by SKAN on D39F/D39G
Local                                                           ;            @ tiny.cc/showmenu
  If (hMenu="WM_ENTERMENULOOP")
    Return True
  Fn := Func("ShowMenu").Bind("WM_ENTERMENULOOP"), n := MenuLoop=0 ? 0 : OnMessage(0x211,Fn,-1)
  DllCall("SetForegroundWindow","Ptr",A_ScriptHwnd)
  R := DllCall("TrackPopupMenu", "Ptr",hMenu, "Int",Flags, "Int",X, "Int",Y, "Int",0
             , "Ptr",A_ScriptHwnd, "Ptr",0, "UInt"),                     OnMessage(0x211,Fn, 0)
  DllCall("PostMessage", "Ptr",A_ScriptHwnd, "Int",0, "Ptr",0, "Ptr",0)
Return R
}


TrayMenuParams() {      ; Original function is TaskbarEdge() by SKAN @ tiny.cc/taskbaredge
Local    ; This modfied version to be passed as parameter to ShowMenu() @ tiny.cc/showmenu
  VarSetCapacity(var,84,0), v:=&var,   DllCall("GetCursorPos","Ptr",v+76)
  X:=NumGet(v+76,"Int"), Y:=NumGet(v+80,"Int"),  NumPut(40,v+0,"Int64")
  hMonitor := DllCall("MonitorFromPoint", "Int64",NumGet(v+76,"Int64"), "Int",0, "Ptr")
  DllCall("GetMonitorInfo", "Ptr",hMonitor, "Ptr",v)
  DllCall("GetWindowRect", "Ptr",WinExist("ahk_class Shell_SecondaryTrayWnd"), "Ptr",v+68)
  DllCall("SubtractRect", "Ptr",v+52, "Ptr",v+4, "Ptr",v+68)
  DllCall("GetWindowRect", "Ptr",WinExist("ahk_class Shell_TrayWnd"), "Ptr",v+36)
  DllCall("SubtractRect", "Ptr",v+20, "Ptr",v+52, "Ptr",v+36)
  Loop % (8, offset:=0)
    v%A_Index% := NumGet(v+0, offset+=4, "Int")
Return ( v3>v7 ? [v7, Y, 0x18] : v4>v8 ? [X, v8, 0x24]
       : v5>v1 ? [v5, Y, 0x10] : v6>v2 ? [X, v6, 0x04] : [0,0,0] )
}
My Scripts and Functions: V1  V2

User avatar
gwarble
Posts: 530
Joined: 30 Sep 2013, 15:01

Re: ShowMenu() : in center screen.

Post by gwarble » 08 Mar 2022, 16:18

Thanks again SKAN, another function of yours I love and have used successfully for a while now in all my projects with a tray menu so it doesn't overlap the taskbar


One issue maybe you can help with or help me understand, when I try to use it for popup menus that insert text into another window, I get a different result than the Menu, Name, Show command and was wondering if you knew why or had a workaround.

With the normal Menu,, Show command, the menu is shown and the currently active window is made inactive. When a menu item is selected, the menu disappears, the previously active window is once again made active visually, and then the command executes (ie Send, text into the active window)

With a ShowMenu(), the menu is shown and the currently active window is made inactive, but when an item is selected there is no longer a visibly active window and the previously active window needs activating manually.

Any ideas on making this act more like the command? Any downside to removing the DllCall("SetForegroundWindow","Ptr",A_ScriptHwnd) ? can't click away from menu

Code: Select all

#NoEnv
#SingleInstance, Force
Msgbox, Press F1 in an edit box to insert symbol.`nThe first menu is Menu,,Show, second is ShowMenu(), not working as desired
CoordMode, Mouse, Screen
Menu, Symbol, Add, Ø, Send
Menu, Symbol, Add, °, Send
Menu, Symbol, Add, ×, Send
Return

F1::
 Menu, Symbol, Show
 ShowMenu(MenuGetHandle("Symbol"), False, A_ScreenWidth/2, A_ScreenHeight/2, 0x14 )
Return

Send:
 Send, {%A_ThisMenuItem%}
Return

ShowMenu(hMenu, MenuLoop:=0, X:=0, Y:=0, Flags:=0) {            ; Ver 0.61 by SKAN on D39F/D39G
Local                                                           ;            @ tiny.cc/showmenu
  If (hMenu="WM_ENTERMENULOOP")
    Return True
  Fn := Func("ShowMenu").Bind("WM_ENTERMENULOOP"), n := MenuLoop=0 ? 0 : OnMessage(0x211,Fn,-1)
  DllCall("SetForegroundWindow","Ptr",A_ScriptHwnd)     
  R := DllCall("TrackPopupMenu", "Ptr",hMenu, "Int",Flags, "Int",X, "Int",Y, "Int",0
             , "Ptr",A_ScriptHwnd, "Ptr",0, "UInt"),                     OnMessage(0x211,Fn, 0)
  DllCall("PostMessage", "Ptr",A_ScriptHwnd, "Int",0, "Ptr",0, "Ptr",0)
Return R
}

EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .

User avatar
rommmcek
Posts: 1502
Joined: 15 Aug 2014, 15:18

Re: ShowMenu() : in center screen.

Post by rommmcek » 09 Mar 2022, 07:54

My workaround for such happening is:
Spoiler
May be it will be useful until better solution!
Last edited by rommmcek on 09 Mar 2022, 14:42, edited 1 time in total.

User avatar
gwarble
Posts: 530
Joined: 30 Sep 2013, 15:01

Re: ShowMenu() : in center screen.

Post by gwarble » 09 Mar 2022, 13:38

I tried adding hLast := DllCall("GetForegroundWindow") and finishing with DllCall("SetForegroundWindow","Ptr",hLast) which does restore the active window cleanly but the Menu action takes place before the activation.

rommmcek, I will try your method, thanks.
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .

User avatar
rommmcek
Posts: 1502
Joined: 15 Aug 2014, 15:18

Re: ShowMenu() : in center screen.

Post by rommmcek » 09 Mar 2022, 14:41

Your approach works for ShowMenu():
Spoiler
P.s.: I'll edit my previous post because I didn't understand the problem correctly.

User avatar
gwarble
Posts: 530
Joined: 30 Sep 2013, 15:01

Re: ShowMenu() : in center screen.

Post by gwarble » 09 Mar 2022, 15:05

Yes, but I'd like to modify the function to replicate this behavior and not have to modify each subroutine... ie my goal is to replace any Menu,,Show with ShowMenu() and not have to remember active windows or anything scriptside. I have also modified my copy to position at the mouse as default rather than 0,0, and use menu by name or handle... so usage is simply ShowMenu("Name") equivilant to Menu, Name, Show

Code: Select all

ShowMenu(hMenu, MenuLoop:=0, X:="", Y:="", Flags:=0) {            ; Ver 0.61 by SKAN on D39F/D39G
 Local                                                            ;            @ tiny.cc/showmenu
 If (X = "") OR (Y = "") {                                        
  CoordMode, Mouse, Screen                                        ; modified for default mouse position instead of 0,0, like Menu,,Show
  MouseGetPos, X, Y
 }                                                                ; modified for menu by name or handle
 hMenu := DllCall("IsWindow", "Ptr", hMenu) ? hMenu : MenuGetHandle(hMenu)
 ...
 
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .

User avatar
rommmcek
Posts: 1502
Joined: 15 Aug 2014, 15:18

Re: ShowMenu() : in center screen.

Post by rommmcek » 10 Mar 2022, 12:29

This seems to work as you wish:
Spoiler

elbitjusticiero
Posts: 91
Joined: 06 May 2017, 11:07

Re: ShowMenu() : in center screen.

Post by elbitjusticiero » 14 Jun 2022, 06:54

I'm having problems with this function. I can't seem to destroy the menu with the Escape key, and pressing an accelerator key to activate a menu item often doesn't work -- I can only use the mouse. But this happens at random -- other times it works.

Skrell
Posts: 335
Joined: 23 Jan 2014, 12:05

Re: ShowMenu() : in center screen.

Post by Skrell » 07 Aug 2023, 10:13

Any way to shrink the width of the menu to minimize the amount of "empty" space?
image.png
image.png (6.99 KiB) Viewed 3479 times

User avatar
SKAN
Posts: 1619
Joined: 29 Sep 2013, 16:58

Re: ShowMenu() : in center screen.

Post by SKAN » 07 Aug 2023, 18:48

Skrell wrote:
07 Aug 2023, 10:13
Any way to shrink the width of the menu to minimize the amount of "empty" space?

image.png
The width of the menu is affected by the max string length of a menu item.
Try reducing the menu item length

User avatar
SKAN
Posts: 1619
Joined: 29 Sep 2013, 16:58

Re: ShowMenu() : in center screen.

Post by SKAN » 07 Aug 2023, 18:54

elbitjusticiero wrote:
14 Jun 2022, 06:54
I'm having problems with this function. I can't seem to destroy the menu with the Escape key, and pressing an accelerator key to activate a menu item often doesn't work -- I can only use the mouse. But this happens at random -- other times it works.
I am able to reproduce the said behavior, only if I run the following script by double clicking it from file explorer

Code: Select all

; d:\testMenu.ahk

#NoEnv
#SingleInstance, Force
Menu, Tray, Show
If I launch it from context menu "Run Script" / "Open with" or d:\testMenu.ahk from Run dialog, the behavior is normal.

User avatar
SKAN
Posts: 1619
Joined: 29 Sep 2013, 16:58

Re: ShowMenu() : in center screen.

Post by SKAN » 08 Aug 2023, 22:17

@gwarble
 
gwarble wrote:
09 Mar 2022, 13:38
I tried adding hLast := DllCall("GetForegroundWindow") and finishing with DllCall("SetForegroundWindow","Ptr",hLast) which does restore the active window cleanly but the Menu action takes place before the activation.
 
The one way to delay Menu action until restoring foreground window seems to use Critcal, On.
The side effect is Modeless menu (via WM_ENTERMENULOOP) isn't possible with Critcal, On!
 
Here is the modified version as you require:

Code: Select all

#NoEnv
#SingleInstance, Force

Menu, Symbol, Add, % Chr(216), Send
Menu, Symbol, Add, % Chr(176), Send
Menu, Symbol, Add, % Chr(215), Send
Return

F2:: ShowMenu("Symbol",,, 0x14)

Send:
 SendInput, {%A_ThisMenuItem%}
Return

ShowMenu(hMenu, X := "", Y := "", Flags := 0) {   ;  ShowMenu v0.63 by SKAN on D39F/D689 @ autohotkey.com/r?t=81064
Local
  If   hMenu Is Not Integer
       hMenu := MenuGetHandle(hMenu)

  If ( X=""  Or  Y="" ) {
       CMM := A_CoordModeMouse
       CoordMode, Mouse, Screen
       MouseGetPos, XX, YY
       CoordMode, Mouse, %CMM%
       X := X="" ? XX : X
       Y := Y="" ? YY : Y
  }

  Flags    &= ~0x180                              ;  disallow flags TPM_RETURNCMD (0x100), TPM_NONOTIFY (0x80)
  pWnd     := DllCall("User32\GetForegroundWindow", "Ptr")
  DllCall("User32\SetForegroundWindow","Ptr",mWnd := A_ScriptHwnd)

  Old_IsCritical := A_IsCritical
  Critical On

  R := DllCall("User32\TrackPopupMenuEx", "Ptr",hMenu, "UInt",Flags, "Int",X, "Int",Y, "Ptr",mWnd, "Ptr",0, "UInt")
  Sleep, 50                                       ;  wait for WM_COMMAND... DllCall("WaitMessage") too late, sucks!
  DllCall("User32\PostMessage", "Ptr",mWnd, "Int",0, "Ptr",0, "Ptr",0)

  If DllCall("User32\GetForegroundWindow", "Ptr") = mWnd  And Not  WinActive("ahk_id " mWnd)
     DllCall("User32\SetForegroundWindow","Ptr",pWnd)

  Critical %Old_IsCritical%
Return R
}

User avatar
gwarble
Posts: 530
Joined: 30 Sep 2013, 15:01

Re: ShowMenu() : in center screen.

Post by gwarble » 08 Aug 2023, 23:09

thanks for looking into this, I love this functions ability to avoid the taskbar, I will try out your new version in place of what I use daily...
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .

Skrell
Posts: 335
Joined: 23 Jan 2014, 12:05

Re: ShowMenu() : in center screen.

Post by Skrell » 17 Oct 2023, 13:28

Any way to choose which monitor the menu is drawn on?

Post Reply

Return to “Scripts and Functions (v1)”