[wx-dev] Alignment and size problems with owner draw menus (Windows)

Wx_widgets developer mailing list, post #22,144
Author:
Date:
Subject:
 David Barnard
 2008-07-15 11:48:09
 [wx-dev] Alignment and size problems with owner draw menus (Windows)
I found an inconsistency with MSW owner draw menu items, so I tried to
correct it. I found some odd fudge factors, and I was able to remove
most of them. When I had done so, owner draw menus lined up perfectly
with the native items. A patch is attached.

Here are some line by line comments:

Index: src/msw/checklst.cpp
===================================================================
--- src/msw/checklst.cpp (revision 54577)
+++ src/msw/checklst.cpp (working copy)
@@ -160,7 +160,7 @@
// done in OnMeasure while they are used only in OnDraw and we
// know that there will always be OnMeasure before OnDraw

- SetMarginWidth(::GetSystemMetrics(SM_CXMENUCHECK) - 2);
+ SetMarginWidth(::GetSystemMetrics(SM_CXMENUCHECK) + 4);

SetBackgroundColour(pParent->GetBackgroundColour());
}

This was presumably a hack to deal with the problem in the OwnerDraw
class. The fixed offset is dictated by the drawing code (there is a
fixed 2 pixel margin around the checkbox).

Index: src/msw/ownerdrw.cpp
===================================================================
--- src/msw/ownerdrw.cpp (revision 54577)
+++ src/msw/ownerdrw.cpp (working copy)
@@ -111,9 +111,9 @@

static void DoInitMetrics()
{
- // iMenuHeight is the menu bar height and the menu items are less tall,
- // although I don't know by how much -- below is the value
for my system
- ms_systemMenuHeight = GetNCM().iMenuHeight - 4;
+ // iMenuHeight is the menu bar height and the menu items are less tall.
+ NONCLIENTMETRICS nc = GetNCM();
+ ms_systemMenuHeight = nc.iMenuHeight - 2 * nc.iBorderWidth;

wxASSERT_MSG( ms_systemMenuHeight > 0,
"menu height should be positive" );

This is the default menu item height.
This value looks right to me, and it renders perfectly.

@@ -259,10 +259,6 @@
dc.GetTextExtent(str, &w, &h);
*pwidth = w;
*pheight = h;
-
- // add space at the end of the menu for the submenu expansion arrow
- // this will also allow offsetting the accel string from the right edge
- *pwidth += GetMarginWidth() + 16;
}
else // don't draw the text, just the bitmap (if any)
{

These next few changes are in OnMeasureItem()
I couldn't work out what this padding was for. It was causing huge
blank spaces at the end of menu items. I suspect it was added to
compensate for one of the other bugs.

@@ -290,10 +286,11 @@
*pwidth += widthBmp;
}
}
+ else
+ {
+ *pwidth += GetMarginWidth();
+ }

- // add a 4-pixel separator, otherwise menus look cluttered
- *pwidth += 4;
-
// make sure that this item is at least as tall as the system menu height
const size_t heightStd = wxMSWSystemMenuFontModule::GetSystemMenuHeight();
if ( *pheight < heightStd )

Another hardcoded fudge factor. I think it was needed because the
margin had been omitted altogether when there is no bitmap.

@@ -374,18 +371,21 @@
COLORREF colOldText = ::SetTextColor(hdc, colText),
colOldBack = ::SetBkColor(hdc, colBack);

- // *2, as in wxSYS_EDGE_Y
- int margin = GetMarginWidth() + 2 *
wxSystemSettings::GetMetric(wxSYS_EDGE_X);
+ int margin = GetMarginWidth();

// select the font and draw the text
// ---------------------------------


// determine where to draw and leave space for a check-mark.
+ int xText = rc.x + margin;
+
// + 1 pixel to separate the edge from the highlight rectangle
- int xText = rc.x + margin + 1;
+ if (draw_bitmap_edge)
+ {
+ xText++;
+ }

-
// using native API because it recognizes '&'
if ( IsOwnerDrawn() )
{

This is in OnDrawItem()
I don't know why wxSYS_EDGE_X was used here. The 1 pixel gap is only
needed when the bitmap border is being drawn.

@@ -411,8 +411,6 @@

wxString strMenuText = m_strName.BeforeFirst('\t');

- xText += 3; // separate text from the highlight rectangle
-
SIZE sizeRect;
::GetTextExtentPoint32(hdc, strMenuText.c_str(),
strMenuText.length(), &sizeRect);

No idea what this was for. It broke the alignment between native and
owner draw items.


@@ -432,7 +430,7 @@
(LPARAM)strMenuText.wx_str(),
strMenuText.length(),
xText,
- rc.y + (rc.GetHeight() - sizeRect.cy + 1)/2, // centre vertically
+ rc.y + (rc.GetHeight() - sizeRect.cy + 0.5)/2, // centre vertically
rc.GetWidth() - margin,
sizeRect.cy,
flags

This tiny change centres the text in the same way as the native menu
items. I think.

I have tested these changes against all the samples I can think of.
Regards

David Barnard
Index: src/msw/checklst.cpp
===================================================================
--- src/msw/checklst.cpp (revision 54577)
+++ src/msw/checklst.cpp (working copy)
@@ -160,7 +160,7 @@
// done in OnMeasure while they are used only in OnDraw and we
// know that there will always be OnMeasure before OnDraw

- SetMarginWidth(::GetSystemMetrics(SM_CXMENUCHECK) - 2);
+ SetMarginWidth(::GetSystemMetrics(SM_CXMENUCHECK) + 4);

SetBackgroundColour(pParent->GetBackgroundColour());
}
Index: src/msw/ownerdrw.cpp
===================================================================
--- src/msw/ownerdrw.cpp (revision 54577)
+++ src/msw/ownerdrw.cpp (working copy)
@@ -111,9 +111,9 @@

static void DoInitMetrics()
{
- // iMenuHeight is the menu bar height and the menu items are less tall,
- // although I don't know by how much -- below is the value for my system
- ms_systemMenuHeight = GetNCM().iMenuHeight - 4;
+ // iMenuHeight is the menu bar height and the menu items are less tall.
+ NONCLIENTMETRICS nc = GetNCM();
+ ms_systemMenuHeight = nc.iMenuHeight - 2 * nc.iBorderWidth;

wxASSERT_MSG( ms_systemMenuHeight > 0,
"menu height should be positive" );
@@ -259,10 +259,6 @@
dc.GetTextExtent(str, &w, &h);
*pwidth = w;
*pheight = h;
-
- // add space at the end of the menu for the submenu expansion arrow
- // this will also allow offsetting the accel string from the right edge
- *pwidth += GetMarginWidth() + 16;
}
else // don't draw the text, just the bitmap (if any)
{
@@ -290,10 +286,11 @@
*pwidth += widthBmp;
}
}
+ else
+ {
+ *pwidth += GetMarginWidth();
+ }

- // add a 4-pixel separator, otherwise menus look cluttered
- *pwidth += 4;
-
// make sure that this item is at least as tall as the system menu height
const size_t heightStd = wxMSWSystemMenuFontModule::GetSystemMenuHeight();
if ( *pheight < heightStd )
@@ -374,18 +371,21 @@
COLORREF colOldText = ::SetTextColor(hdc, colText),
colOldBack = ::SetBkColor(hdc, colBack);

- // *2, as in wxSYS_EDGE_Y
- int margin = GetMarginWidth() + 2 * wxSystemSettings::GetMetric(wxSYS_EDGE_X);
+ int margin = GetMarginWidth();

// select the font and draw the text
// ---------------------------------


// determine where to draw and leave space for a check-mark.
+ int xText = rc.x + margin;
+
// + 1 pixel to separate the edge from the highlight rectangle
- int xText = rc.x + margin + 1;
+ if (draw_bitmap_edge)
+ {
+ xText++;
+ }

-
// using native API because it recognizes '&'
if ( IsOwnerDrawn() )
{
@@ -411,8 +411,6 @@

wxString strMenuText = m_strName.BeforeFirst('\t');

- xText += 3; // separate text from the highlight rectangle
-
SIZE sizeRect;
::GetTextExtentPoint32(hdc, strMenuText.c_str(), strMenuText.length(), &sizeRect);

@@ -432,7 +430,7 @@
(LPARAM)strMenuText.wx_str(),
strMenuText.length(),
xText,
- rc.y + (rc.GetHeight() - sizeRect.cy + 1)/2, // centre vertically
+ rc.y + (rc.GetHeight() - sizeRect.cy + 0.5)/2, // centre vertically
rc.GetWidth() - margin,
sizeRect.cy,
flags_______________________________________________
wx-dev mailing list
[email protected]
http://lists.wxwidgets.org/mailman/listinfo/wx-dev