News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Prevent Menu Wrap

Started by dedndave, February 23, 2011, 04:17:35 PM

Previous topic - Next topic

dedndave

just to update this thread a little - in case anyone uses it as reference...

i was playing with the windows appearance settings - font size
as previously discussed, the "magic number" is 8 for the normal sized menu font
for large font, it is 9
for extra large font, it is 11
now, you can go into SystemParametersInfo, SPI_GETNONCLIENTMETRICS and retrieve the LOGFONT for menu text
(i am guessing the magic number is actually the width of a cell)
however, it is likely to be a mapped font, and you have to take more steps to find cell width
all that is way too much code for this - lol
my choice - i use the method i posted above, only i use a magic number of 12   :bg
;****************************************************************************************

MbMin   PROC

;set minimum window width value
;the resulting value is used in reply to WM_GETMINMAXINFO,
;to set the MINMAXINFO.ptMinTrackSize.x value
;this prevents the menu bar from wrapping
;
;MAIN_MENU_ITEMS = number of items in main menu
;
;MENUBARINFO STRUCT
; cbSize           dd ?
; rcBar            RECT <>
;   left             dd ?
;   top              dd ?
;   right            dd ?
;   bottom           dd ?
; hMenu            dd ?
; hwndMenu         dd ?
; fBarFocused      dw ?
; fFocused         dw ?
;
;POINT STRUCT
; x                dd ?
; y                dd ?

        push    edi
        push    sizeof MENUBARINFO
        pop     edi
        pushad
        INVOKE  GetMenuBarInfo,hWin,OBJID_MENU,MAIN_MENU_ITEMS,esp
        add     esp,12
        INVOKE  ScreenToClient,hWin,esp
        pop     ecx                                      ;x
        pop     edx                                      ;y
        add     ecx,edi
        add     edx,edi
        push    edx                                      ;bottom
        push    ecx                                      ;right
        push    edi                                      ;top
        push    edi                                      ;left
        mov     eax,esp
        INVOKE  AdjustWindowRect,eax,WS_THICKFRAME,TRUE
        pop     edx                                      ;left
        pop     ecx                                      ;top
        pop     eax                                      ;right
        sub     eax,edx
        add     eax,12                                   ;magic number
        add     esp,16
        mov     MinWidth,eax
        pop     edi
        ret

MbMin   ENDP

;****************************************************************************************


dedndave

#16
here it is in "bloat mode"   :bg
for those who do not like my push/pops
;****************************************************************************************

MbMin   PROC    USES EDI

;set minimum window width value
;the resulting value is used in reply to WM_GETMINMAXINFO,
;to set the MINMAXINFO.ptMinTrackSize.x value
;this prevents the menu bar from wrapping
;
;MAIN_MENU_ITEMS = number of items in main menu
;
;MENUBARINFO STRUCT
; cbSize           dd ?
; rcBar            RECT <>
;   left             dd ?
;   top              dd ?
;   right            dd ?
;   bottom           dd ?
; hMenu            dd ?
; hwndMenu         dd ?
; fBarFocused      dw ?
; fFocused         dw ?

        LOCAL   mbi:MENUBARINFO

        mov     edi,sizeof MENUBARINFO
        mov     mbi.cbSize,edi
        INVOKE  GetMenuBarInfo,hWin,OBJID_MENU,MAIN_MENU_ITEMS,addr mbi
        INVOKE  ScreenToClient,hWin,addr mbi.rcBar.right
        mov     mbi.rcBar.left,edi
        add     mbi.rcBar.right,edi
        mov     mbi.rcBar.top,edi
        add     mbi.rcBar.bottom,edi
        INVOKE  AdjustWindowRect,addr mbi.rcBar,WS_THICKFRAME,TRUE
        mov     eax,mbi.rcBar.right
        sub     eax,mbi.rcBar.left
        add     eax,12                                              ;magic number
        mov     MinWidth,eax
        ret

MbMin   ENDP

;****************************************************************************************

dedndave

still playing with this   :bg

i wrote some code to see what rectangle values are returned at different times
i got some very interesting results...
minimized, during WM_CREATE: (application started with minimized window)

    window rect: 176 232 877 627
menu bar "File": 180 262 208 281

--------------------------

maximized, during WM_CREATE: (application started with maximized window)

    window rect: 176 232 877 627
menu bar "File": 180 262 208 281

--------------------------

minimized:

    window rect: -32000 -32000 -31840 -31969
menu bar "File": -31996 -31970 -31968 -31951

--------------------------

normal:

    window rect: 176 232 877 627
menu bar "File": 180 262 208 281

--------------------------

maximized:

    window rect: -4 -4 1284 1028
menu bar "File": 0 26 28 45


it seems that, during WM_CREATE, the coordinates returned are those for a normal window, no mattter how the app is started
this is good to know   :U
while the values returned for a minimized window are offset by some strange neg value, they can still be used to some degree
you can still use them to calculate the size of the border and title bar, as well as the size of the menu item
the minimized window rectangle reflects the size on the task bar
minimizing multiple instances does not tell you the task bar position, though   :P

this applies to a window created with the WS_VISIBLE style bit set and...
CW_USEDEFAULT used for the x parameter
SW_SHOWWINDOW used for the y parameter
numeric values used for the nWidth and nHeight parameters
results may be different for different sets of parameters
but, for the most part, i think the conclusions are valid

dedndave

back to the code....
MichaelW made an interesting observation...
Quote... for the menu item coordinates to be useable the window must
initially be wide enough to prevent the menu from wrapping.

my solution is to tally the total widths for all items on the main menu
i get the same result whether the menu is wrapped or not
i will post the updated code sometime in the near future   :P

dedndave

ok - here we go   :P
this one works, even if the menu is initially wrapped
it will work for the normal, large, and extra large fonts in XP appearance settings

;MinWidth Include File - DednDave 7, 2011

;#############################################################################################################

MinWidth PROTO  :HWND,:UINT,:UINT

;#############################################################################################################

IFNDEF  MENUBARINFO
MENUBARINFO STRUCT
  cbSize           dd ?
  rcBar          RECT <>
  hMenu            dd ?
  hwndMenu         dd ?
  fBarFocused      dw ?
  fFocused         dw ?
MENUBARINFO ENDS
ENDIF

;#############################################################################################################

MinWidth PROC   uses esi edi hWnd:HWND,dwStyle:UINT,nMenuItemQty:UINT

;Find Minimum Window Width to Prevent Menu Wrap
;DednDave 7, 2011
;
;May be used in WM_CREATE, WM_THEMECHANGED, and in WM_SETTINGCHANGE when wParam = SPI_SETNONCLIENTMETRICS.
;The result value may then be saved and used in WM_GETMINMAXINFO to set MINMAXINFO.ptMinTrackSize.x.
;
;Call With:         hWnd = Main window handle
;                dwStyle = Style used for AdjustWindowRect; If WS_OVERLAPPEDWINDOW, use WS_THICKFRAME
;           nMenuItemQty = Number of main menu items
;
;  Returns:          EAX = Minimum window width
;
; Example: INVOKE  MinWidth,hWnd,WS_THICKFRAME,MAIN_MENU_ITEMS
;          mov     MinWindowWidth,eax

        mov     eax,nMenuItemQty
        or      eax,eax
        jz      MinWd1

        pushad
        xchg    eax,edi
        push    sizeof MENUBARINFO
        xor     esi,esi

MinWd0: INVOKE  GetMenuBarInfo,hWnd,OBJID_MENU,edi,esp
        add     esi,[esp].MENUBARINFO.rcBar.right
        sub     esi,[esp].MENUBARINFO.rcBar.left
        dec     edi
        jnz     MinWd0

        lea     eax,[esp].MENUBARINFO.hMenu
        INVOKE  GetWindowRect,hWnd,eax
        pop     edi                                      ;discard cbSize
        pop     eax                                      ;EAX = mb.left
        pop     ecx                                      ;ECX = mb.top
        pop     edi                                      ;discard mb.right
        pop     edx                                      ;EDX = mb.bottom
        pop     edi                                      ;EDI = wr.left
        sub     eax,edi                                  ;EAX = cr.left
        pop     edi                                      ;EDI = wr.top
        sub     ecx,edi                                  ;ECX = cr.top
        sub     edx,edi                                  ;EDX = cr.bottom
        add     esi,eax                                  ;ESI = cr.right
        push    edx                                      ;cr.bottom
        push    esi                                      ;cr.right
        push    ecx                                      ;cr.top
        push    eax                                      ;cr.left
        mov     edi,esp
        push    12                                       ;stack balance/magic number
        INVOKE  AdjustWindowRect,edi,dwStyle,TRUE
        pop     edx                                      ;EDX = 12
        pop     edi                                      ;discard wr.left (0)
        pop     ecx                                      ;ECX = wr.top (0)
        pop     eax                                      ;EAX = wr.right
        add     esp,edx
        add     eax,edx

MinWd1: ret

MinWidth ENDP

;#############################################################################################################

;End of WinWidth.inc

dedndave

ok - still working on this - lol
it seems that, during WM_THEMECHANGED and WM_SETTINGCHANGE,
there is a brief period of time when GetMenuBarInfo does not return correct rectangle values
furthermore, if i try to wait for the values to be correct, the system hangs   :bg
it is waiting for me to return 0 before completing the transition
strange, but i suppose it allows applications to make global changes during the switch

so - the above code works great, except when we need it to work   :lol
i will have to examine the message queue to see what all goes on for a solution

dedndave

as i said before, this is not as simple as it ought to be   :bg

i am using Display Properties, Appearance tab, Font Size to test my code
when the change is made, there is a sequence of messages....
hWin     unknown (31Bh)                 wParam: 00000000 lParam: 00000000
hWin     WM_WINDOWPOSCHANGING           wParam: 00000000 lParam: 0012F8B4
hWin     WM_NCCALCSIZE                  wParam: 00000001 lParam: 0012F888
hWin     WM_WINDOWPOSCHANGED            wParam: 00000000 lParam: 0012F8B4
hWin     WM_WINDOWPOSCHANGING           wParam: 00000000 lParam: 0012F938
hWin     WM_NCCALCSIZE                  wParam: 00000001 lParam: 0012F90C
hWin     WM_ERASEBKGND                  wParam: 92011145 lParam: 00000000
hWin     WM_WINDOWPOSCHANGED            wParam: 00000000 lParam: 0012F938
hWin     WM_NCCALCSIZE                  wParam: 00000001 lParam: 0012F568
hWin     unknown (31Bh)                 wParam: 0001001E lParam: 001001D1
hWin     WM_PAINT                       wParam: 00000000 lParam: 00000000
hWin     WM_CTLCOLORSCROLLBAR           wParam: 7C0104DC lParam: 003902E2
hWin     WM_CTLCOLORSCROLLBAR           wParam: 2401100D lParam: 004302FE
hWin     WM_SYSCOLORCHANGE              wParam: 00000000 lParam: 00000000
hWin     WM_SETTINGCHANGE               wParam: 00000000 lParam: 0012FF58
hWin     WM_SETTINGCHANGE               wParam: 00000000 lParam: 0012FF58
hWin     WM_SETTINGCHANGE               wParam: 0000000D lParam: 0012FF64
hWin     WM_SETTINGCHANGE               wParam: 00001023 lParam: 0012FF64
hWin     WM_SETTINGCHANGE               wParam: 00000018 lParam: 0012FF64
hWin     WM_SETTINGCHANGE               wParam: 00000018 lParam: 0012FF64
hWin     WM_GETMINMAXINFO               wParam: 00000000 lParam: 0012FD34
hWin     WM_NCCALCSIZE                  wParam: 00000001 lParam: 0012FF18
hWin     WM_WINDOWPOSCHANGED            wParam: 00000000 lParam: 0012FF44
hWin     WM_MOVE                        wParam: 00000000 lParam: 0038000E
hWin     WM_SIZE                        wParam: 00000000 lParam: 01240202
hWin     WM_CTLCOLORSCROLLBAR           wParam: D7010D70 lParam: 004302FE
hWin     WM_CTLCOLORSCROLLBAR           wParam: D7010D70 lParam: 003902E2
hWin     WM_NCCALCSIZE                  wParam: 00000001 lParam: 0012FB74
hWin     WM_SETICON                     wParam: 00000003 lParam: 00000000
hWin     WM_SETICON                     wParam: 00000003 lParam: 00000000
hWin     WM_GETICON                     wParam: 00000002 lParam: 00000000
hWin     WM_GETICON                     wParam: 00000000 lParam: 00000000
hWin     WM_GETICON                     wParam: 00000001 lParam: 00000000
hWin     WM_WINDOWPOSCHANGING           wParam: 00000000 lParam: 0012FD78
hWin     WM_NCCALCSIZE                  wParam: 00000001 lParam: 0012FD4C
hWin     WM_WINDOWPOSCHANGED            wParam: 00000000 lParam: 0012FD78
hWin     WM_WINDOWPOSCHANGED            wParam: 00000000 lParam: 0012FD78
hWin     WM_NCPAINT                     wParam: 00000001 lParam: 00000000
hWin     WM_SYNCPAINT                   wParam: 00000004 lParam: 00000000
hWin     WM_CTLCOLORSCROLLBAR           wParam: B90103E8 lParam: 003902E2
hWin     WM_CTLCOLORSCROLLBAR           wParam: B90103E8 lParam: 004302FE
hWin     WM_CTLCOLORSCROLLBAR           wParam: B90103E8 lParam: 004302FE


there are a few problems
one is, if you call GetMenuBarInfo during WM_SETTINGCHANGE code,
the size has not yet updated
if it is the middle of the change, the function may return RECT coordinates of 0
i tried "defering" the update until WM_GETMINMAXINFO
same thing happens
there is no clearly documented point in the sequence that may be used to know the change has taken affect

i wrote some code that creates a thread
it waits a period of time, then gets menu bar info, then updates
it works ok - but it does not seem a reliable method
it may work on one system with a certain set of time constants - but not on the next system
this is something that i may come back to later

for now........
;WM_GETMINMAXINFO

MinMx0: mov     edx,lParam
        mov     [edx].MINMAXINFO.ptMinTrackSize.x,208
        xor     eax,eax
        ret


:lol