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

i would like to prevent the main menu from wrapping
i can easily process WM_GETMINMAXINFO messages to set a minimum window width
and i can empirically find a minimum width of some constant value that works
however, i would like to know what the required width is that will display the menu without wrapping
there must be a way to find the value programmatically so that it works for different themes, menu text sizes, display resolutions, etc
any ideas ??????




the attachment is the above PNG image renamed as a ZIP

dedndave

i guess i can play with MenuItemFromPoint
not very elegant   :P

qWord

FPU in a trice: SmplMath
It's that simple!

dedndave

thanks qWord   :U

i had looked at that one and did not see what i wanted, at first (coordinates of the menu bar won't do it)
now i see that i can specify a menu item and get coordinates - that may do the trick

jj2007

Put together a "menu titles" string of format File__Edit__Format__Other and estimate its width. Should be precise enough...

dedndave

not sure i understand what you mean, Jochen

jj2007

Concat the menu titles, then use GetTextMetrics and the tmAveCharWidth member of the TEXTMETRIC structure.

qWord

hi,
here an short example showing how it could be done:
Quoteinclude masm32rt.inc
.686p
.mmx
.xmm

MENUBARINFO struct
    cbSize      DWORD   ?
    rcBar       RECT    <>
    hMenu       HMENU   ?
    hwndMenu    HWND    ?
    flags       SDWORD  ? ; fBarFocused=1 , fFocused=2
    ;fBarFocused : 1
    ;fFocused   : 1
MENUBARINFO ends
PMENUBARINFO typedef ptr MENUBARINFO

.data?
    hInstance   HINSTANCE   ?
    hwnd        HWND        ?
    cxFrame     DWORD       ?
    cxMin       DWORD       ?
.code

WndProc proto hWnd:HWND,uMgs:UINT,wParam:WPARAM,lParam:LPARAM

main proc
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG

    mov hInstance,rv(GetModuleHandle,0)
    mov wc.hInstance,eax
    mov wc.cbSize,SIZEOF wc
    mov wc.style,CS_HREDRAW or CS_VREDRAW or CS_SAVEBITS
    mov wc.lpfnWndProc,OFFSET WndProc
    mov wc.cbClsExtra,0
    mov wc.cbWndExtra,0
    mov wc.hIcon,rv(LoadIcon,0,IDI_APPLICATION)
    mov wc.hIconSm,eax
    mov wc.hCursor,rv(LoadCursor,0,IDC_ARROW)
    mov wc.lpszMenuName,0
    mov wc.hbrBackground,rv(GetStockObject,WHITE_BRUSH);
    mov wc.lpszClassName,chr$("Win32Wnd")
    invoke RegisterClassEx,ADDR wc
   
    mov esi,rv(CreateMenu)
    fn AppendMenu,esi,MF_STRING,1,"entry1"
    fn AppendMenu,esi,MF_STRING,2,"entry2"
    fn AppendMenu,esi,MF_STRING,3,"entry3"
    fn AppendMenu,esi,MF_STRING,4,"entry4"
   
    mov ebx,ASM(mov edi,rv(GetSystemMetrics,SM_CXSCREEN))
    mov ecx,rv(GetSystemMetrics,SM_CYSCREEN)
    shr ebx,1
    shr eax,1
    shr edi,2
    shr ecx,2
    mov esi,rv(CreateWindowEx,0,wc.lpszClassName,"Win32Wnd",WS_VISIBLE or WS_SYSMENU or WS_MAXIMIZEBOX or WS_MINIMIZEBOX or WS_SIZEBOX,edi,ecx,ebx,eax,0,esi,hInstance,0)
    mov hwnd,eax
    invoke UpdateWindow,esi
   
    .while 1
        invoke GetMessage,ADDR msg,0,0,0
        .break .if !eax || eax == -1
        invoke TranslateMessage,ADDR msg
        invoke DispatchMessage,ADDR msg     
    .endw

    invoke ExitProcess,0
   
main endp

WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
LOCAL rect[2]:RECT
LOCAL mbi:MENUBARINFO

    .if uMsg == WM_CLOSE
        invoke PostQuitMessage,0
    .elseif uMsg == WM_CREATE
        invoke GetClientRect,hWnd,ADDR rect
        invoke GetWindowRect,hWnd,ADDR rect[16]
        mov edx,rect[16].right
        sub edx,rect[16].left
        sub edx,rect.right
        mov cxFrame,edx
        mov mbi.cbSize,SIZEOF mbi
        invoke GetMenuBarInfo,hWnd,OBJID_MENU,4,ADDR mbi    ; coord. are relativ to screen!
        mov eax,mbi.rcBar.right
        sub eax,rect[16].left
        add eax,cxFrame
        mov cxMin,eax   
    .elseif uMsg == WM_GETMINMAXINFO
        mov edx,lParam
        m2m [edx].MINMAXINFO.ptMinTrackSize.x,cxMin
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
   
    xor eax,eax
    ret
   
WndProc endp
end main
FPU in a trice: SmplMath
It's that simple!

MichaelW

A different version based on an in-memory dialog:

;==============================================================================
; Build as console app.
;==============================================================================
    include \masm32\include\masm32rt.inc
;==============================================================================

printf MACRO format:REQ, args:VARARG
    IFNB <args>
        invoke crt_printf, cfm$(format), args
    ELSE
        invoke crt_printf, cfm$(format)
    ENDIF
    EXITM <>
ENDM

;==============================================================================

MENUBARINFO STRUCT
    cbSize    DWORD ?
    rcBar     RECT  <>
    hMenu     HMENU ?
    hwndMenu  HWND  ?
    focused   DWORD ?
MENUBARINFO ENDS

;==============================================================================

IDM_FILE equ 100
IDM_EDIT equ 101
IDM_VIEW equ 102
IDM_HELP equ 103

;==============================================================================
    .data
        hMenu     dd 0
        hFileMenu dd 0
        hEditMenu dd 0
        hViewMenu dd 0
        hHelpMenu dd 0
        mbi       MENUBARINFO <SIZEOF MENUBARINFO>
    .code
;==============================================================================

SetClientSize proc uses ebx hwnd:HWND, pixelWidth:DWORD, pixelHeight:DWORD

    LOCAL rcc:RECT, rcw:RECT

    invoke GetClientRect, hwnd, ADDR rcc
    invoke GetWindowRect, hwnd, ADDR rcw

    mov ecx, rcw.right
    sub ecx, rcw.left       ; ecx = window width - 1

    mov eax, pixelWidth
    dec eax                 ; eax = pixelWidth - 1
    mov ebx, rcc.right      ; ebx = client width - 1
    sub ebx, eax            ; ebx = difference
    sub ecx, ebx            ; adjust width

    mov edx, rcw.bottom     ; edx = window height - 1
    sub edx, rcw.top

    mov eax, pixelHeight
    dec eax                 ; eax = pixelHeight - 1
    mov ebx, rcc.bottom     ; ebx = client height - 1
    sub ebx, eax            ; ebx = difference
    sub edx, ebx            ; adjust height

    invoke MoveWindow, hwnd, rcw.left, rcw.top, ecx, edx, TRUE

    ret

SetClientSize endp

;==============================================================================

DlgProc proc hwndDlg:dword, uMsg:dword, wParam:dword, lParam:dword

    LOCAL rc:RECT
    LOCAL pt:POINT

    SWITCH uMsg

      CASE WM_INITDIALOG

          invoke CreateMenu
          mov hMenu, eax
          invoke CreateMenu
          mov hFileMenu, eax
          invoke CreateMenu
          mov hEditMenu, eax
          invoke CreateMenu
          mov hViewMenu, eax
          invoke CreateMenu
          mov hHelpMenu, eax
          invoke AppendMenu, hMenu, MF_POPUP, hFileMenu, chr$("&File")
          invoke AppendMenu, hMenu, MF_POPUP, hEditMenu, chr$("&Edit")
          invoke AppendMenu, hMenu, MF_POPUP, hViewMenu, chr$("&View")
          invoke AppendMenu, hMenu, MF_POPUP, hHelpMenu, chr$("&Help")
          invoke SetMenu, hwndDlg, hMenu

          ;--------------------------------------------------------
          ; Get the menu info for the fourth item on the menu bar.
          ;--------------------------------------------------------

          invoke GetMenuBarInfo, hwndDlg, OBJID_MENU, 4, ADDR mbi
          printf("GetMenuBarInfo: %d\n", eax)

          ;----------------------------------------------------------------
          ; Convert the returned screen coordinates to client coordinates.
          ; The Y coordinate will be discarded.
          ;----------------------------------------------------------------

          printf("mbi.rcBar.right: %d\n", mbi.rcBar.right)
          push mbi.rcBar.right
          pop pt.x
          push 0
          pop pt.y
          invoke ScreenToClient, hwndDlg, ADDR pt
          printf("pt.x: %d\n", pt.x)

          ;----------------------------------------------------------
          ; Get the current client coordinates, need rc.bottom only.
          ;----------------------------------------------------------

          invoke GetClientRect, hwndDlg, ADDR rc
          printf("rc.right: %d\n", rc.right)
          printf("rc.bottom: %d\n", rc.bottom)

          ;------------------------------------
          ; Adjust rc.bottom to client height.
          ;------------------------------------

          inc rc.bottom

          ;----------------------------------------------------
          ; Under Windows 2000 this adjuatment is necessary to
          ; prevent the menu from wrapping.
          ;----------------------------------------------------

          add pt.x, 12
          printf("pt.x+12: %d\n", pt.x)

          ;---------------------------------------------------
          ; Set the new width and leave the height unchanged.
          ;---------------------------------------------------

          invoke SetClientSize, hwndDlg, pt.x, rc.bottom

          ;---------------
          ; Check result.
          ;---------------

          invoke GetClientRect, hwndDlg, ADDR rc
          printf("rc.right after sizing: %d\n", rc.right)
          printf("rc.bottom after sizing: %d\n", rc.bottom)


      CASE WM_COMMAND

          SWITCH wParam

              CASE IDCANCEL

                  invoke EndDialog, hwndDlg, 0

          ENDSW

      CASE WM_CLOSE

        invoke EndDialog, hwndDlg, 0

    ENDSW

    xor eax, eax
    ret

DlgProc endp

;==============================================================================
start:
;==============================================================================

    Dialog "Test", "MS Sans Serif",10, \
           WS_OVERLAPPED or WS_SYSMENU or DS_CENTER, \
           0,0,0,120,60,1024

    invoke GetModuleHandle, NULL

    CallModalDialog eax,0,DlgProc,NULL

    exit
;==============================================================================
end start
eschew obfuscation

dedndave

thanks guys   :U

on qWord's version, i had to add 2 pixels - i will try to figure out why when i get home

MichaelW

When I was investigating the reason for the 12-pixel pad, using a sizeable window, I determined that the system maintains a minimum 14-pixel space between the right edge of the rightmost menu "button" and the client area border. BTW, the value 12 includes the 1-pixel adjustment from the zero-based rc.right coordinate to the client width, so the minimum pad is actually 11 pixels.
eschew obfuscation

dedndave

well - i think the border comes into play
qWord's calculation accounts for one border - but not the other
when we set the limit in the MINMAXINFO structure, we want a window size - not a client size
at any rate, to avoid all the messy calculations, i was going to try this:

1) get the right edge of the last menu item
2) subtract that from the left edge of the first menu item
3) use the AdjustWindowRect function to get the minimum window width
4) process WM_GETMINMAXINFO

steps 1-3 are done at init

had a busy day - off to bed for me
i'll play with it tomorrow

dedndave

well - i got it to work - more or less - lol

first, let's start with the MENUBARINFO structure
MSDN defines it this way:
typedef struct tagMENUBARINFO {
  DWORD cbSize;
  RECT  rcBar;
  HMENU hMenu;
  HWND  hwndMenu;
  BOOL  fBarFocused;
  BOOL  fFocused;
} MENUBARINFO, *PMENUBARINFO;

in the community content...
struct definition not correct
from WinUser.h:
...
BOOL fBarFocused:1; // bar, popup has the focus
BOOL fFocused:1; // item has the focus
...

that probably makes sense, if you are a C programmer - lol
the way i see it, fBarFocused and fFocused are bit-fields
although, i am not sure what the correct name is to assign to the dword
i am going to use fFocus
qWord is close to being right - the values are 1 or 2 - so bits 0 and 1 are the fields
there is probably a correct way to define that using FIELD

next, the GetMenuBarInfo function does indeed return screen coordinates
the documentation uses the term "window coordinates" - a little misleading, i think
if i look at the left edge of the first menu item, it gives you the left edge of the client area
so - no need to call that function twice - i use ScreenToClient instead

now for the pad
if i grab the right coordinate (which is actually the right + 1 in RECT structures),
i can add Michael's magic number of 14 and get working results
however, a more appropriate way to do it is to call AdjustWindowRect
remember - we have client width - we want window width - and, we want to account for differently themed borders
oddly enough, AdjustWindowRect does not like it if you use 0 for the left and top client rectangle
so, adding some constant to all the values makes it work correctly
any constant will work (so long as it is larger than the theme border width), as it gets subtracted out
if you use the menu item rectangle as is, it should always work

so.....
1) INVOKE GetMenuBarInfo,hWnd,OBJID_MENU,4,addr mbi
2) INVOKE ScreenToClient,hWnd,addr mbi.rcBar.right
3) set the left and top to 8, and add 8 to mbi.rcBar.right
4) INVOKE AdjustWindowRect,addr mbi.rcBar,WS_THICKFRAME,TRUE
the function does not allow using WS_OVERLAPPED, so i used a style that is allowed and has the same border
5) subtract the left edge from the right edge
6) add the corrected pad of 8 to obtain the minimum window width
        push    8
        INVOKE  GetMenuBarInfo,hWnd,OBJID_MENU,4,addr mbi
        pop     edi
        INVOKE  ScreenToClient,hWnd,addr mbi.rcBar.right
        mov     mbi.rcBar.left,edi
        mov     mbi.rcBar.top,edi
        add     mbi.rcBar.right,edi
        INVOKE  AdjustWindowRect,addr mbi.rcBar,WS_THICKFRAME,TRUE
        mov     eax,mbi.rcBar.right
        sub     eax,mbi.rcBar.left
        add     eax,edi
        mov     cxMinimum,eax

MichaelW

I used client coordinates and sized the client area simply because the menu bar needs to fit the client area. Also, for the menu item coordinates to be useable the window must initially be wide enough to prevent the menu from wrapping.
eschew obfuscation

dedndave

yes - your version is a little different
we are processing the WM_GETMINMAXINFO message to set the minimum window width
the code i posted above is the init code to determine what value to set it to
qWord's WndProc has the message process code - it's very simple