#include "wx/stockitem.h"
#ifndef WX_PRECOMP
+ #include "wx/app.h"
+ #include "wx/dcmemory.h"
#include "wx/font.h"
#include "wx/bitmap.h"
#include "wx/settings.h"
Margins CheckMargin; // popup check margins
Margins CheckBgMargin; // popup check background margins
+ Margins ArrowMargin; // popup submenu arrow margins
+
Margins SeparatorMargin; // popup separator margins
SIZE CheckSize; // popup check size metric
+ SIZE ArrowSize; // popup submenu arrow size metric
SIZE SeparatorSize; // popup separator size metric
- int AccelBorder; // popup border space between
- // item text and accelerator
int TextBorder; // popup border space between
// item text and gutter
+ int AccelBorder; // popup border space between
+ // item text and accelerator
+
+ int ArrowBorder; // popup border space between
+ // item accelerator and submenu arrow
+
+ int Offset; // system added space at the end of the menu,
+ // add this offset for remove the extra space
+
wxFont Font; // default menu font
bool AlwaysShowCues; // must keyboard cues always be shown?
static const MenuDrawData* Get()
{
+ // notice that s_menuData can't be created as a global variable because
+ // it needs a window to initialize and no windows exist at the time of
+ // globals initialization yet
+ if ( !ms_instance )
+ {
+ static MenuDrawData s_menuData;
+ ms_instance = &s_menuData;
+ }
+
#if wxUSE_UXTHEME
bool theme = MenuLayout() == FullTheme;
if ( ms_instance->Theme != theme )
MenuDrawData()
{
- ms_instance = this;
Init();
}
MenuDrawData* MenuDrawData::ms_instance = NULL;
-MenuDrawData s_menuData;
-
void MenuDrawData::Init()
{
#if wxUSE_UXTHEME
TMT_CONTENTMARGINS, NULL,
reinterpret_cast<MARGINS*>(&CheckBgMargin));
+ theme->GetThemeMargins(hTheme, NULL, MENU_POPUPSUBMENU, 0,
+ TMT_CONTENTMARGINS, NULL,
+ reinterpret_cast<MARGINS*>(&ArrowMargin));
+
theme->GetThemeMargins(hTheme, NULL, MENU_POPUPSEPARATOR, 0,
TMT_SIZINGMARGINS, NULL,
reinterpret_cast<MARGINS*>(&SeparatorMargin));
theme->GetThemePartSize(hTheme, NULL, MENU_POPUPCHECK, 0,
NULL, TS_TRUE, &CheckSize);
+ theme->GetThemePartSize(hTheme, NULL, MENU_POPUPSUBMENU, 0,
+ NULL, TS_TRUE, &ArrowSize);
+
theme->GetThemePartSize(hTheme, NULL, MENU_POPUPSEPARATOR, 0,
NULL, TS_TRUE, &SeparatorSize);
- theme->GetThemeInt(hTheme, MENU_POPUPBORDERS, 0, TMT_BORDERSIZE, &AccelBorder);
theme->GetThemeInt(hTheme, MENU_POPUPBACKGROUND, 0, TMT_BORDERSIZE, &TextBorder);
+ AccelBorder = 34;
+ ArrowBorder = 0;
+
+ Offset = -14;
+
wxNativeFontInfo fontInfo;
theme->GetThemeSysFont(hTheme, TMT_MENUFONT, &fontInfo.lf);
Font = wxFont(fontInfo);
CheckSize.cx = ::GetSystemMetrics(SM_CXMENUCHECK);
CheckSize.cy = ::GetSystemMetrics(SM_CYMENUCHECK);
+ ArrowMargin = Margins();
+
+ ArrowSize = CheckSize;
+
// separator height with margins
int sepFullSize = metrics.iMenuHeight / 2;
TextBorder = 0;
AccelBorder = 8;
+ ArrowBorder = 6;
+
+ Offset = -12;
Font = wxFont(wxNativeFontInfo(metrics.lfMenuFont));
#if wxUSE_OWNER_DRAWN
+int wxMenuItem::MeasureAccelWidth() const
+{
+ wxString accel = GetItemLabel().AfterFirst(wxT('\t'));
+
+ wxMemoryDC dc;
+ wxFont font;
+ GetFontToUse(font);
+ dc.SetFont(font);
+
+ wxCoord w;
+ dc.GetTextExtent(accel, &w, NULL);
+
+ return w;
+}
+
wxString wxMenuItem::GetName() const
{
return GetItemLabelText();
return true;
}
- wxString str = GetItemLabel();
-
- // text and accel separator char removal
- str.Replace(wxT('\t'), wxEmptyString);
+ wxString str = GetName();
wxMemoryDC dc;
wxFont font;
wxCoord w, h;
dc.GetTextExtent(str, &w, &h);
- *width = w + data->TextBorder + data->AccelBorder;
+ *width = data->TextBorder + w + data->AccelBorder;
*height = h;
- // system added space at the end of the menu for the submenu expansion
- // arrow, but we must add a 4-pixel separator for better apperance
- *width += 4;
+ w = m_parentMenu->GetMaxAccelWidth();
+ if ( w > 0 )
+ *width += w + data->ArrowBorder;
+
+ *width += data->Offset;
+ *width += data->ArrowMargin.left + data->ArrowSize.cx + data->ArrowMargin.right;
}
else // don't draw the text, just the bitmap (if any)
{
if ( IsOwnerDrawn() )
{
- // width of menu icon in ownerdrawn menu
+ // width of menu icon with margins in ownerdrawn menu
// if any bitmap is not set, the width of space reserved for icon
// image is equal to the width of std check mark,
// if bitmap is set, then the width is set to the width of the widest
RECT rect;
wxCopyRectToRECT(rc, rect);
- int imgWidth = wxMax(GetMarginWidth(), data->CheckSize.cx)
- + data->CheckMargin.left + data->CheckMargin.right;
+ int imgWidth = wxMax(GetMarginWidth(), data->CheckSize.cx);
if ( IsOwnerDrawn() )
{
CopyRect(&rcGutter, &rcSelection);
rcGutter.right = data->ItemMargin.left
+ data->CheckBgMargin.left
+ + data->CheckMargin.left
+ imgWidth
+ + data->CheckMargin.right
+ data->CheckBgMargin.right;
CopyRect(&rcText, &rcSelection);
rcText.left = rcGutter.right + data->TextBorder;
+ // we draw the text label vertically centered, but this results in it
+ // being 1px too low compared to native menus for some reason, fix it
+ if ( data->MenuLayout() != MenuDrawData::FullTheme )
+ rcText.top--;
+
#if wxUSE_UXTHEME
wxUxThemeEngine* theme = MenuDrawData::GetUxThemeEngine();
if ( theme )
(stat & wxODDisabled) && !(stat & wxODSelected) )
flags |= DSS_DISABLED;
- // right align accel string with right edge of menu
- // (offset by the margin width)
+ int x = rcText.right - data->ArrowMargin.left
+ - data->ArrowSize.cx
+ - data->ArrowMargin.right
+ - data->ArrowBorder;
+
+ // right align accel on FullTheme menu, left otherwise
+ if ( data->MenuLayout() == MenuDrawData::FullTheme)
+ x -= accelSize.cx;
+ else
+ x -= m_parentMenu->GetMaxAccelWidth();
- int x = rcText.right - 16 - accelSize.cx;
int y = rcText.top + (rcText.bottom - rcText.top - accelSize.cy) / 2;
::DrawState(hdc, NULL, NULL, (LPARAM)accel.wx_str(),
RECT rcImg;
SetRect(&rcImg,
- rect.left + data->ItemMargin.left + data->CheckBgMargin.left,
- rect.top + data->ItemMargin.top + data->CheckBgMargin.top,
- rect.left + data->ItemMargin.left + data->CheckBgMargin.left
- + imgWidth,
- rect.bottom - data->ItemMargin.bottom - data->CheckBgMargin.bottom);
+ rect.left + data->ItemMargin.left
+ + data->CheckBgMargin.left
+ + data->CheckMargin.left,
+ rect.top + data->ItemMargin.top
+ + data->CheckBgMargin.top
+ + data->CheckMargin.top,
+ rect.left + data->ItemMargin.left
+ + data->CheckBgMargin.left
+ + data->CheckMargin.left
+ + imgWidth,
+ rect.bottom - data->ItemMargin.bottom
+ - data->CheckBgMargin.bottom
+ - data->CheckMargin.bottom);
if ( IsCheckable() && !m_bmpChecked.Ok() )
{
if ( stat & wxODChecked )
{
- #if wxUSE_UXTHEME
- wxUxThemeEngine* theme = MenuDrawData::GetUxThemeEngine();
- if ( theme )
- {
- wxUxThemeHandle hTheme(GetMenu()->GetWindow(), L"MENU");
-
- POPUPCHECKBACKGROUNDSTATES stateCheckBg = (stat & wxODDisabled)
- ? MCB_DISABLED
- : MCB_NORMAL;
-
- theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPCHECKBACKGROUND,
- stateCheckBg, &rcImg, NULL);
-
- // check mark will be drawn centered on the background
-
- POPUPCHECKSTATES stateCheck = (stat & wxODDisabled)
- ? MC_CHECKMARKDISABLED
- : MC_CHECKMARKNORMAL;
-
- theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPCHECK,
- stateCheck, &rcImg, NULL);
- }
- else
- #endif // wxUSE_UXTHEME
- {
- int cx = imgWidth;
- int cy = rcImg.bottom - rcImg.top;
-
- // what goes on: DrawFrameControl creates a b/w mask,
- // then we copy it to screen to have right colors
-
- // first create a monochrome bitmap in a memory DC
- HDC hdcMem = ::CreateCompatibleDC(hdc);
- HBITMAP hbmpCheck = ::CreateBitmap(cx, cy, 1, 1, 0);
- ::SelectObject(hdcMem, hbmpCheck);
-
- // then draw a check mark into it
- RECT rect = { 0, 0, cx, cy };
- if ( rc.GetHeight() > 0 )
- {
- ::DrawFrameControl(hdcMem, &rect, DFC_MENU, DFCS_MENUCHECK);
- }
-
- // finally copy it to screen DC and clean up
- ::BitBlt(hdc, rcImg.left, rcImg.top, cx, cy, hdcMem, 0, 0, SRCCOPY);
-
- ::DeleteDC(hdcMem);
- }
+ DrawStdCheckMark((WXHDC)hdc, &rcImg, stat);
}
}
else
}
+namespace
+{
+
+// helper function for draw coloured check mark
+void DrawColorCheckMark(HDC hdc, int x, int y, int cx, int cy, HDC hdcCheckMask, int idxColor)
+{
+ const COLORREF colBlack = RGB(0, 0, 0);
+ const COLORREF colWhite = RGB(255, 255, 255);
+
+ COLORREF colOldText = ::SetTextColor(hdc, colBlack);
+ COLORREF colOldBack = ::SetBkColor(hdc, colWhite);
+ int prevMode = SetBkMode(hdc, TRANSPARENT);
+
+ // memory DC for color bitmap
+ MemoryHDC hdcMem(hdc);
+ CompatibleBitmap hbmpMem(hdc, cx, cy);
+ SelectInHDC selMem(hdcMem, hbmpMem);
+
+ RECT rect = { 0, 0, cx, cy };
+ ::FillRect(hdcMem, &rect, ::GetSysColorBrush(idxColor));
+
+ const COLORREF colCheck = ::GetSysColor(idxColor);
+ if ( colCheck == colWhite )
+ {
+ ::BitBlt(hdc, x, y, cx, cy, hdcCheckMask, 0, 0, MERGEPAINT);
+ ::BitBlt(hdc, x, y, cx, cy, hdcMem, 0, 0, SRCAND);
+ }
+ else
+ {
+ if ( colCheck != colBlack )
+ {
+ const DWORD ROP_DSna = 0x00220326; // dest = (NOT src) AND dest
+ ::BitBlt(hdcMem, 0, 0, cx, cy, hdcCheckMask, 0, 0, ROP_DSna);
+ }
+
+ ::BitBlt(hdc, x, y, cx, cy, hdcCheckMask, 0, 0, SRCAND);
+ ::BitBlt(hdc, x, y, cx, cy, hdcMem, 0, 0, SRCPAINT);
+ }
+
+ ::SetBkMode(hdc, prevMode);
+ ::SetBkColor(hdc, colOldBack);
+ ::SetTextColor(hdc, colOldText);
+}
+
+} // anonymous namespace
+
+void wxMenuItem::DrawStdCheckMark(WXHDC hdc_, const RECT* rc, wxODStatus stat)
+{
+ HDC hdc = (HDC)hdc_;
+
+#if wxUSE_UXTHEME
+ wxUxThemeEngine* theme = MenuDrawData::GetUxThemeEngine();
+ if ( theme )
+ {
+ wxUxThemeHandle hTheme(GetMenu()->GetWindow(), L"MENU");
+
+ const MenuDrawData* data = MenuDrawData::Get();
+
+ // rect for background must be without check margins
+ RECT rcBg;
+ SetRect(&rcBg,
+ rc->left - data->CheckMargin.left,
+ rc->top - data->CheckMargin.top,
+ rc->right + data->CheckMargin.right,
+ rc->bottom + data->CheckMargin.bottom);
+
+ POPUPCHECKBACKGROUNDSTATES stateCheckBg = (stat & wxODDisabled)
+ ? MCB_DISABLED
+ : MCB_NORMAL;
+
+ theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPCHECKBACKGROUND,
+ stateCheckBg, &rcBg, NULL);
+
+ POPUPCHECKSTATES stateCheck;
+ if ( GetKind() == wxITEM_CHECK )
+ {
+ stateCheck = (stat & wxODDisabled) ? MC_CHECKMARKDISABLED
+ : MC_CHECKMARKNORMAL;
+ }
+ else
+ {
+ stateCheck = (stat & wxODDisabled) ? MC_BULLETDISABLED
+ : MC_BULLETNORMAL;
+ }
+
+ theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPCHECK,
+ stateCheck, rc, NULL);
+ }
+ else
+#endif // wxUSE_UXTHEME
+ {
+ int cx = rc->right - rc->left;
+ int cy = rc->bottom - rc->top;
+
+ // first create mask of check mark
+ MemoryHDC hdcMask(hdc);
+ MonoBitmap hbmpMask(cx, cy);
+ SelectInHDC selMask(hdcMask,hbmpMask);
+
+ // then draw a check mark into it
+ UINT stateCheck = (GetKind() == wxITEM_CHECK) ? DFCS_MENUCHECK
+ : DFCS_MENUBULLET;
+ RECT rect = { 0, 0, cx, cy };
+ ::DrawFrameControl(hdcMask, &rect, DFC_MENU, stateCheck);
+
+ // first draw shadow if disabled
+ if ( (stat & wxODDisabled) && !(stat & wxODSelected) )
+ {
+ DrawColorCheckMark(hdc, rc->left + 1, rc->top + 1,
+ cx, cy, hdcMask, COLOR_3DHILIGHT);
+ }
+
+ // then draw a check mark
+ int color = COLOR_MENUTEXT;
+ if ( stat & wxODDisabled )
+ color = COLOR_BTNSHADOW;
+ else if ( stat & wxODSelected )
+ color = COLOR_HIGHLIGHTTEXT;
+
+ DrawColorCheckMark(hdc, rc->left, rc->top, cx, cy, hdcMask, color);
+ }
+}
+
void wxMenuItem::GetFontToUse(wxFont& font) const
{
font = GetFont();