lundi 20 avril 2015

How to properly measure & display owner drawn context menu item with a checkmark?

I'm simply trying to add small color swatches to my context menu (displayed via the TrackPopupMenu API.) Here's a Photoshopped version of what I'm trying to achieve:

enter image description here

As far as I understand the default menu does not support it. Btw, the sample above (without color swatches) was generated by doing this:

MENUITEMINFO mii = {0};
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING;
mii.fType = MFT_STRING;
mii.wID = ID_1_MARKER_01 + m;
mii.dwTypeData = L"Marker";
mii.cch = TSIZEOF(L"Marker");
mii.fState = m == 1 ? MFS_CHECKED : MFS_ENABLED;
if(m == 2)
    mii.fState |= MFS_GRAYED;

VERIFY(::InsertMenuItem(hMenu, ID_1_BEFORE, FALSE, &mii));

So I discovered that I need to use MFT_OWNERDRAW style to draw menu items myself, but that's where the problems begin.

I changed my code to display my menu as such:

MENUITEMINFO mii = {0};
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE;
mii.fType = MFT_OWNERDRAW;
mii.wID = ID_1_MARKER_01 + m;
mii.dwItemData = MARKER_ID_01 + m;
mii.fState = m == 1 ? MFS_CHECKED : MFS_ENABLED;
if(m == 2)
    mii.fState |= MFS_GRAYED;

VERIFY(::InsertMenuItem(hMenu, ID_1_BEFORE, FALSE, &mii));

then I needed to override WM_MEASUREITEM and WM_DRAWITEM messages. But when I do it with the code that I'll show below, here's what I get:

enter image description here

So please bear with me. I have several questions on this topic:

1) While processing WM_MEASUREITEM how am I supposed to know the size of text if they do not supply neither DC nor HWND for the menu? In other words, if I do this, the size of the menus is wrong:

#define TSIZEOF(f) ((sizeof(f) - sizeof(TCHAR)) / sizeof(TCHAR))

//hwnd = HWND supplied in WM_MEASUREITEM notification
HDC hDC = ::GetDC(hwnd);
HGDIOBJ hOldFont = ::SelectObject(hDC, ::SendMessage(hwnd, WM_GETFONT, 0, 0));

SIZE szTxt = {0};
::GetTextExtentPoint32(hDC, 
    L"Marker", 
    TSIZEOF(L"Marker"), 
    &szTxt);

//lpmis = MEASUREITEMSTRUCT*
lpmis->itemWidth = szTxt.cx;
lpmis->itemHeight = szTxt.cy;

::SelectObject(hDC, hOldFont);
::ReleaseDC(hwnd, hDC);

2) Then while processing WM_DRAWITEM how do I know the offset to begin drawing text on the left? If I do this, my menus aren't offset enough to the right (as you can see from the screenshot above):

int nCheckW = ::GetSystemMetrics(SM_CXMENUCHECK);

//lpdis = DRAWITEMSTRUCT*
::ExtTextOut(lpdis->hDC, 
    lpdis->rcItem.left + nCheckW, 
    lpdis->rcItem.top, 
    ETO_OPAQUE, 
            &lpdis->rcItem, 
    L"Marker", 
    TSIZEOF(L"Marker"), 
    NULL);

3) And lastly how do I draw that default checkbox on the left of the menu item?

Aucun commentaire:

Enregistrer un commentaire