Use theme functions for drawing owner-drawn menus.

This makes the menu items with custom attributes or bitmaps look more native,
especially on post-XP systems.

Closes #11420.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@63223 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2010-01-23 13:21:36 +00:00
parent d08504dfa5
commit aa4919edd1
5 changed files with 623 additions and 205 deletions

View File

@@ -115,6 +115,7 @@ public:
protected: protected:
virtual void GetFontToUse(wxFont& font) const; virtual void GetFontToUse(wxFont& font) const;
virtual void GetColourToUse(wxODStatus stat, wxColour& colText, wxColour& colBack) const;
#endif // wxUSE_OWNER_DRAWN #endif // wxUSE_OWNER_DRAWN
@@ -140,11 +141,6 @@ private:
wxBitmap m_bmpChecked, // bitmap to put near the item wxBitmap m_bmpChecked, // bitmap to put near the item
m_bmpUnchecked, // (checked is used also for 'uncheckable' items) m_bmpUnchecked, // (checked is used also for 'uncheckable' items)
m_bmpDisabled; m_bmpDisabled;
// static variables for cache some system settings
static wxFont ms_systemMenuFont;
static size_t ms_systemMenuHeight;
static bool ms_alwaysShowCues;
#endif // wxUSE_OWNER_DRAWN #endif // wxUSE_OWNER_DRAWN
DECLARE_DYNAMIC_CLASS_NO_COPY(wxMenuItem) DECLARE_DYNAMIC_CLASS_NO_COPY(wxMenuItem)

View File

@@ -69,6 +69,8 @@ enum
Menu_Bitmap, Menu_Bitmap2, Menu_Bitmap, Menu_Bitmap2,
Menu_Submenu, Menu_Sub1, Menu_Sub2, Menu_Sub3, Menu_Submenu, Menu_Sub1, Menu_Sub2, Menu_Sub3,
Menu_Toggle, Menu_About, Menu_Toggle, Menu_About,
Menu_Drawn1, Menu_Drawn2, Menu_Drawn3, Menu_Drawn4, Menu_Drawn5,
Menu_Native1, Menu_Native2, Menu_Native3, Menu_Native4, Menu_Native5,
Control_First = 1000, Control_First = 1000,
Control_Listbox, Control_Listbox2 Control_Listbox, Control_Listbox2
}; };
@@ -198,9 +200,62 @@ void OwnerDrawnFrame::InitMenu()
wxITEM_NORMAL); wxITEM_NORMAL);
file_menu->Append(pItem); file_menu->Append(pItem);
wxMenu* drawn_menu = new wxMenu;
pItem = new wxMenuItem(drawn_menu, Menu_Drawn1, wxT("&Menu item\tCtrl+K"));
drawn_menu->Append(pItem);
drawn_menu->AppendSeparator();
pItem = new wxMenuItem(drawn_menu, Menu_Drawn2, wxT("&Cheked item"),
wxT("check/uncheck me!"), wxITEM_CHECK);
drawn_menu->Append(pItem);
drawn_menu->Check(Menu_Drawn2, true);
pItem = new wxMenuItem(drawn_menu, Menu_Drawn3, wxT("&Radio item"),
wxT("check/uncheck me!"), wxITEM_RADIO);
drawn_menu->Append(pItem);
drawn_menu->AppendSeparator();
pItem = new wxMenuItem(drawn_menu, Menu_Drawn4, wxT("&Disabled item\tCtrl+RatherLongAccel"),
wxT("disabled item"));
pItem->Enable(false);
drawn_menu->Append(pItem);
pItem = new wxMenuItem(drawn_menu, Menu_Drawn5, wxT("&Other\tCtrl+O"), wxT("other item"));
pItem->SetTextColour(*wxRED);
drawn_menu->Append(pItem);
wxMenu* native_menu = new wxMenu;
pItem = new wxMenuItem(native_menu, Menu_Native1, wxT("&Menu item\tCtrl+K"));
native_menu->Append(pItem);
native_menu->AppendSeparator();
pItem = new wxMenuItem(native_menu, Menu_Native2, wxT("&Cheked item"),
wxT("check/uncheck me!"), wxITEM_CHECK);
native_menu->Append(pItem);
native_menu->Check(Menu_Native2, true);
pItem = new wxMenuItem(native_menu, Menu_Native3, wxT("&Radio item"),
wxT("check/uncheck me!"), wxITEM_RADIO);
native_menu->Append(pItem);
native_menu->AppendSeparator();
pItem = new wxMenuItem(native_menu, Menu_Native4, wxT("&Disabled item\tCtrl+RatherLongAccel"),
wxT("disabled item"));
pItem->Enable(false);
native_menu->Append(pItem);
pItem = new wxMenuItem(native_menu, Menu_Native5, wxT("&Other\tCtrl+O"), wxT("other item"));
native_menu->Append(pItem);
wxMenuBar *menu_bar = new wxMenuBar; wxMenuBar *menu_bar = new wxMenuBar;
menu_bar->Append(file_menu, wxT("&File")); menu_bar->Append(file_menu, wxT("&File"));
menu_bar->Append(drawn_menu, wxT("&Drawn"));
menu_bar->Append(native_menu, wxT("&Native"));
SetMenuBar(menu_bar); SetMenuBar(menu_bar);
} }

View File

@@ -86,8 +86,17 @@ void wxOwnerDrawnBase::GetColourToUse(wxODStatus stat, wxColour& colText, wxColo
else else
{ {
// fall back to default colors if none explicitly specified // fall back to default colors if none explicitly specified
if ( stat & wxODDisabled )
{
colText = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
}
else
{
colText = m_colText.Ok() ? m_colText colText = m_colText.Ok() ? m_colText
: wxSystemSettings::GetColour(wxSYS_COLOUR_MENUTEXT); : wxSystemSettings::GetColour(wxSYS_COLOUR_MENUTEXT);
}
colBack = m_colBack.Ok() ? m_colBack colBack = m_colBack.Ok() ? m_colBack
: wxSystemSettings::GetColour(wxSYS_COLOUR_MENU); : wxSystemSettings::GetColour(wxSYS_COLOUR_MENU);
} }

View File

@@ -112,7 +112,8 @@ void SetDefaultMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
// make the given menu item owner-drawn // make the given menu item owner-drawn
void SetOwnerDrawnMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu), void SetOwnerDrawnMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
UINT WXUNUSED_IN_WINCE(id), UINT WXUNUSED_IN_WINCE(id),
ULONG_PTR WXUNUSED_IN_WINCE(data)) ULONG_PTR WXUNUSED_IN_WINCE(data),
BOOL WXUNUSED_IN_WINCE(byPositon = FALSE))
{ {
#ifndef __WXWINCE__ #ifndef __WXWINCE__
MENUITEMINFO mii; MENUITEMINFO mii;
@@ -122,7 +123,10 @@ void SetOwnerDrawnMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
mii.fType = MFT_OWNERDRAW; mii.fType = MFT_OWNERDRAW;
mii.dwItemData = data; mii.dwItemData = data;
if ( !::SetMenuItemInfo(hmenu, id, FALSE, &mii) ) if ( reinterpret_cast<wxMenuItem*>(data)->IsSeparator() )
mii.fType |= MFT_SEPARATOR;
if ( !::SetMenuItemInfo(hmenu, id, byPositon, &mii) )
{ {
wxLogLastError(wxT("SetMenuItemInfo")); wxLogLastError(wxT("SetMenuItemInfo"));
} }
@@ -496,7 +500,7 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
// Under older systems mixing owner-drawn and non-owner-drawn items results // Under older systems mixing owner-drawn and non-owner-drawn items results
// in inconsistent margins, so we force this one to be owner-drawn if any // in inconsistent margins, so we force this one to be owner-drawn if any
// other items already are. // other items already are.
if ( m_ownerDrawn && !pItem->IsSeparator() ) if ( m_ownerDrawn )
pItem->SetOwnerDrawn(true); pItem->SetOwnerDrawn(true);
#endif // wxUSE_OWNER_DRAWN #endif // wxUSE_OWNER_DRAWN
@@ -506,7 +510,7 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
{ {
#ifndef __DMC__ #ifndef __DMC__
if ( !m_ownerDrawn ) if ( !m_ownerDrawn && !pItem->IsSeparator() )
{ {
// MIIM_BITMAP only works under WinME/2000+ so we always use owner // MIIM_BITMAP only works under WinME/2000+ so we always use owner
// drawn item under the previous versions and we also have to use // drawn item under the previous versions and we also have to use
@@ -631,23 +635,25 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
// make other item ownerdrawn and update margin width for equals alignment // make other item ownerdrawn and update margin width for equals alignment
if ( !m_ownerDrawn || updateAllMargins ) if ( !m_ownerDrawn || updateAllMargins )
{ {
// we must use position in SetOwnerDrawnMenuItem because
// all separators have the same id
int pos = 0;
wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst(); wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
while (node) while (node)
{ {
wxMenuItem* item = node->GetData(); wxMenuItem* item = node->GetData();
if ( !item->IsSeparator() ) if ( !item->IsOwnerDrawn())
{
if ( !item->IsOwnerDrawn() )
{ {
item->SetOwnerDrawn(true); item->SetOwnerDrawn(true);
SetOwnerDrawnMenuItem(GetHmenu(), item->GetMSWId(), SetOwnerDrawnMenuItem(GetHmenu(), pos,
reinterpret_cast<ULONG_PTR>(item)); reinterpret_cast<ULONG_PTR>(item), TRUE);
}
item->SetMarginWidth(m_maxBitmapWidth);
} }
item->SetMarginWidth(m_maxBitmapWidth);
node = node->GetNext(); node = node->GetNext();
pos++;
} }
// set menu as ownerdrawn // set menu as ownerdrawn

View File

@@ -52,6 +52,10 @@
UINT GetMenuState(HMENU hMenu, UINT id, UINT flags) ; UINT GetMenuState(HMENU hMenu, UINT id, UINT flags) ;
#endif #endif
#if wxUSE_UXTHEME
#include "wx/msw/uxtheme.h"
#endif
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// macro // macro
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -76,6 +80,53 @@ UINT GetMenuState(HMENU hMenu, UINT id, UINT flags) ;
#define DSS_HIDEPREFIX 0x0200 #define DSS_HIDEPREFIX 0x0200
#endif #endif
#if wxUSE_UXTHEME
enum MENUPARTS
{
MENU_MENUITEM_TMSCHEMA = 1,
MENU_SEPARATOR_TMSCHEMA = 6,
MENU_POPUPBACKGROUND = 9,
MENU_POPUPBORDERS = 10,
MENU_POPUPCHECK = 11,
MENU_POPUPCHECKBACKGROUND = 12,
MENU_POPUPGUTTER = 13,
MENU_POPUPITEM = 14,
MENU_POPUPSEPARATOR = 15,
MENU_POPUPSUBMENU = 16,
};
enum POPUPITEMSTATES
{
MPI_NORMAL = 1,
MPI_HOT = 2,
MPI_DISABLED = 3,
MPI_DISABLEDHOT = 4,
};
enum POPUPCHECKBACKGROUNDSTATES
{
MCB_DISABLED = 1,
MCB_NORMAL = 2,
MCB_BITMAP = 3,
};
enum POPUPCHECKSTATES
{
MC_CHECKMARKNORMAL = 1,
MC_CHECKMARKDISABLED = 2,
MC_BULLETNORMAL = 3,
MC_BULLETDISABLED = 4,
};
const int TMT_MENUFONT = 803;
const int TMT_BORDERSIZE = 2403;
const int TMT_CONTENTMARGINS = 3602;
const int TMT_SIZINGMARGINS = 3601;
#endif // wxUSE_UXTHEME
#endif // wxUSE_OWNER_DRAWN #endif // wxUSE_OWNER_DRAWN
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -131,11 +182,208 @@ IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
#if wxUSE_OWNER_DRAWN #if wxUSE_OWNER_DRAWN
// these static variables are from the wxMenuItem object for cache namespace
// system settings returned by the Win32 API's SystemParametersInfo() call {
wxFont wxMenuItem::ms_systemMenuFont;
size_t wxMenuItem::ms_systemMenuHeight = 0; // helper class to keep information about metrics and other stuff
bool wxMenuItem::ms_alwaysShowCues = false; // needed for measuring and drawing menu item
class MenuDrawData
{
public:
struct Margins
{
int left;
int right;
int top;
int bottom;
Margins()
: left(0),
right(0),
top(0),
bottom(0)
{}
};
Margins ItemMargin; // popup item margins
Margins CheckMargin; // popup check margins
Margins CheckBgMargin; // popup check background margins
Margins SeparatorMargin; // popup separator margins
SIZE CheckSize; // popup check 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
wxFont Font; // default menu font
bool AlwaysShowCues; // must keyboard cues always be shown?
bool Theme; // is data initialized for FullTheme?
static const MenuDrawData* Get()
{
#if wxUSE_UXTHEME
bool theme = MenuLayout() == FullTheme;
if ( ms_instance->Theme != theme )
ms_instance->Init();
#endif // wxUSE_UXTHEME
return ms_instance;
}
MenuDrawData()
{
ms_instance = this;
Init();
}
// get the theme engine or NULL if themes
// are not available or not supported on menu
static wxUxThemeEngine *GetUxThemeEngine()
{
#if wxUSE_UXTHEME
if ( MenuLayout() == FullTheme )
return wxUxThemeEngine::GetIfActive();
#endif // wxUSE_UXTHEME
return NULL;
}
enum MenuLayoutType
{
FullTheme, // full menu themes (Vista or new)
PseudoTheme, // pseudo menu themes (on XP)
Classic
};
static MenuLayoutType MenuLayout()
{
MenuLayoutType menu = Classic;
#if wxUSE_UXTHEME
if ( wxUxThemeEngine::GetIfActive() != NULL )
{
static wxWinVersion ver = wxGetWinVersion();
if ( ver >= wxWinVersion_Vista )
menu = FullTheme;
else if ( ver == wxWinVersion_XP )
menu = PseudoTheme;
}
#endif // wxUSE_UXTHEME
return menu;
}
private:
void Init();
static MenuDrawData* ms_instance;
};
MenuDrawData* MenuDrawData::ms_instance = NULL;
MenuDrawData s_menuData;
void MenuDrawData::Init()
{
#if wxUSE_UXTHEME
wxUxThemeEngine* theme = GetUxThemeEngine();
if ( theme )
{
wxWindow* window = static_cast<wxApp*>(wxApp::GetInstance())->GetTopWindow();
wxUxThemeHandle hTheme(window, L"MENU");
theme->GetThemeMargins(hTheme, NULL, MENU_POPUPITEM, 0,
TMT_CONTENTMARGINS, NULL,
reinterpret_cast<MARGINS*>(&ItemMargin));
theme->GetThemeMargins(hTheme, NULL, MENU_POPUPCHECK, 0,
TMT_CONTENTMARGINS, NULL,
reinterpret_cast<MARGINS*>(&CheckMargin));
theme->GetThemeMargins(hTheme, NULL, MENU_POPUPCHECKBACKGROUND, 0,
TMT_CONTENTMARGINS, NULL,
reinterpret_cast<MARGINS*>(&CheckBgMargin));
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_POPUPSEPARATOR, 0,
NULL, TS_TRUE, &SeparatorSize);
theme->GetThemeInt(hTheme, MENU_POPUPBORDERS, 0, TMT_BORDERSIZE, &AccelBorder);
theme->GetThemeInt(hTheme, MENU_POPUPBACKGROUND, 0, TMT_BORDERSIZE, &TextBorder);
wxNativeFontInfo fontInfo;
theme->GetThemeSysFont(hTheme, TMT_MENUFONT, &fontInfo.lf);
Font = wxFont(fontInfo);
Theme = true;
// native menu doesn't uses the vertical margins
ItemMargin.top = ItemMargin.bottom = 0;
// native menu uses small top margin for separator
if ( SeparatorMargin.top >= 2 )
SeparatorMargin.top -= 2;
}
else
#endif // wxUSE_UXTHEME
{
const NONCLIENTMETRICS& metrics = wxMSWImpl::GetNonClientMetrics();
ItemMargin = Margins();
CheckMargin.left =
CheckMargin.right = ::GetSystemMetrics(SM_CXEDGE);
CheckMargin.top =
CheckMargin.bottom = ::GetSystemMetrics(SM_CYEDGE);
CheckBgMargin = Margins();
CheckSize.cx = ::GetSystemMetrics(SM_CXMENUCHECK);
CheckSize.cy = ::GetSystemMetrics(SM_CYMENUCHECK);
// separator height with margins
int sepFullSize = metrics.iMenuHeight / 2;
SeparatorMargin.left =
SeparatorMargin.right = 1;
SeparatorMargin.top =
SeparatorMargin.bottom = sepFullSize / 2 - 1;
SeparatorSize.cx = 1;
SeparatorSize.cy = sepFullSize - SeparatorMargin.top - SeparatorMargin.bottom;
TextBorder = 0;
AccelBorder = 8;
Font = wxFont(wxNativeFontInfo(metrics.lfMenuFont));
Theme = false;
}
int value;
if ( ::SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &value, 0) == 0 )
{
// if it's not supported, we must be on an old Windows version
// which always shows them
value = 1;
}
AlwaysShowCues = value == 1;
}
} // anonymous namespace
#endif // wxUSE_OWNER_DRAWN #endif // wxUSE_OWNER_DRAWN
@@ -175,24 +423,6 @@ void wxMenuItem::Init()
#if wxUSE_OWNER_DRAWN #if wxUSE_OWNER_DRAWN
// init static varaibles
if ( !ms_systemMenuHeight )
{
const NONCLIENTMETRICS& metrics = wxMSWImpl::GetNonClientMetrics();
ms_systemMenuFont = wxFont(wxNativeFontInfo(metrics.lfMenuFont));
ms_systemMenuHeight = metrics.iMenuHeight;
if ( ::SystemParametersInfo(SPI_GETKEYBOARDCUES, 0,
&ms_alwaysShowCues, 0) == 0 )
{
// if it's not supported, we must be on an old Windows version
// which always shows them
ms_alwaysShowCues = true;
}
}
// when the color is not valid, wxOwnerDraw takes the default ones. // when the color is not valid, wxOwnerDraw takes the default ones.
// If we set the colors here and they are changed by the user during // If we set the colors here and they are changed by the user during
// the execution, then the colors are not updated until the application // the execution, then the colors are not updated until the application
@@ -460,21 +690,27 @@ wxString wxMenuItem::GetName() const
bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height) bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
{ {
const MenuDrawData* data = MenuDrawData::Get();
if ( IsOwnerDrawn() ) if ( IsOwnerDrawn() )
{ {
*width = data->ItemMargin.left + data->ItemMargin.right;
*height = data->ItemMargin.top + data->ItemMargin.bottom;
wxString str = GetName(); if ( IsSeparator() )
// if we have a valid accel string, then pad out
// the menu string so that the menu and accel string are not
// placed on top of each other.
wxString accel = GetItemLabel().AfterFirst(wxT('\t'));
if ( !accel.empty() )
{ {
str.Pad(str.length()%8); *width += data->SeparatorSize.cx
str += accel; + data->SeparatorMargin.left + data->SeparatorMargin.right;
*height += data->SeparatorSize.cy
+ data->SeparatorMargin.top + data->SeparatorMargin.bottom;
return true;
} }
wxString str = GetItemLabel();
// text and accel separator char removal
str.Replace(wxT('\t'), wxEmptyString);
wxMemoryDC dc; wxMemoryDC dc;
wxFont font; wxFont font;
GetFontToUse(font); GetFontToUse(font);
@@ -482,8 +718,13 @@ bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
wxCoord w, h; wxCoord w, h;
dc.GetTextExtent(str, &w, &h); dc.GetTextExtent(str, &w, &h);
*width = w;
*width = w + data->TextBorder + data->AccelBorder;
*height = h; *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;
} }
else // don't draw the text, just the bitmap (if any) else // don't draw the text, just the bitmap (if any)
{ {
@@ -491,43 +732,49 @@ bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
*height = 0; *height = 0;
} }
// increase size to accommodate bigger bitmaps if necessary // bitmap
if (m_bmpChecked.Ok())
if ( IsOwnerDrawn() )
{ {
// width of menu icon 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
// bitmap in menu (GetMarginWidth()) unless std check mark is wider,
// then it's is set to std mark's width
int imgWidth = wxMax(GetMarginWidth(), data->CheckSize.cx)
+ data->CheckMargin.left + data->CheckMargin.right;
*width += imgWidth + data->CheckBgMargin.left + data->CheckBgMargin.right;
}
if ( m_bmpChecked.IsOk() || m_bmpChecked.IsOk() )
{
// get size of bitmap always return valid value (0 for invalid bitmap),
// so we don't needed check if bitmap is valid ;)
size_t heightBmp = wxMax(m_bmpChecked.GetHeight(), m_bmpUnchecked.GetHeight());
size_t widthtBmp = wxMax(m_bmpChecked.GetWidth(), m_bmpUnchecked.GetWidth());
if ( IsOwnerDrawn() )
{
heightBmp += data->CheckMargin.top + data->CheckMargin.bottom;
}
else
{
// we must allocate enough space for the bitmap
*width += widthtBmp;
}
// Is BMP height larger than text height? // Is BMP height larger than text height?
size_t adjustedHeight = m_bmpChecked.GetHeight(); if ( *height < heightBmp )
if ( *height < adjustedHeight ) *height = heightBmp;
*height = adjustedHeight;
const int widthBmp = m_bmpChecked.GetWidth();
if ( IsOwnerDrawn() )
{
// widen the margin to fit the bitmap if necessary
if ( GetMarginWidth() < widthBmp )
SetMarginWidth(widthBmp);
}
else // we must allocate enough space for the bitmap
{
*width += widthBmp;
}
}
// add a 4-pixel separator, otherwise menus look cluttered
*width += 4;
// notice that this adjustment must be done after (possibly) changing the
// margin width above
if ( IsOwnerDrawn() )
{
// 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
*width += GetMarginWidth() + 16;
} }
// make sure that this item is at least as tall as the system menu height // make sure that this item is at least as tall as the system menu height
if ( *height < ms_systemMenuHeight ) const size_t menuHeight = data->CheckMargin.top + data->CheckMargin.bottom
*height = ms_systemMenuHeight; + data->CheckSize.cy;
if (*height < menuHeight)
*height = menuHeight;
return true; return true;
} }
@@ -535,106 +782,149 @@ bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc, bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
wxODAction WXUNUSED(act), wxODStatus stat) wxODAction WXUNUSED(act), wxODStatus stat)
{ {
const MenuDrawData* data = MenuDrawData::Get();
// this flag determines whether or not an edge will wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
// be drawn around the bitmap. In most "windows classic" HDC hdc = GetHdcOf(*impl);
// applications, a 1-pixel highlight edge is drawn around
// the bitmap of an item when it is selected. However,
// with the new "luna" theme, no edge is drawn around
// the bitmap because the background is white (this applies
// only to "non-XP style" menus w/ bitmaps --
// see IE 6 menus for an example)
bool draw_bitmap_edge = true; RECT rect;
wxCopyRectToRECT(rc, rect);
int imgWidth = wxMax(GetMarginWidth(), data->CheckSize.cx)
+ data->CheckMargin.left + data->CheckMargin.right;
if ( IsOwnerDrawn() )
{
// font and colors to use
wxFont font;
GetFontToUse(font);
// set the colors
// --------------
wxColour colText1, colBack1; wxColour colText1, colBack1;
GetColourToUse(stat, colText1, colBack1); GetColourToUse(stat, colText1, colBack1);
DWORD colText = wxColourToPalRGB(colText1); DWORD colText = wxColourToPalRGB(colText1);
DWORD colBack = wxColourToPalRGB(colBack1); DWORD colBack = wxColourToPalRGB(colBack1);
if ( IsOwnerDrawn() ) // calculate metrics of item parts
RECT rcSelection;
RECT rcSeparator;
RECT rcGutter;
RECT rcText;
SetRect(&rcSelection,
rect.left + data->ItemMargin.left,
rect.top + data->ItemMargin.top,
rect.right - data->ItemMargin.right,
rect.bottom - data->ItemMargin.bottom);
SetRect(&rcSeparator,
rcSelection.left + data->SeparatorMargin.left,
rcSelection.top + data->SeparatorMargin.top,
rcSelection.right - data->SeparatorMargin.right,
rcSelection.bottom - data->SeparatorMargin.bottom);
CopyRect(&rcGutter, &rcSelection);
rcGutter.right = data->ItemMargin.left
+ data->CheckBgMargin.left
+ imgWidth
+ data->CheckBgMargin.right;
CopyRect(&rcText, &rcSelection);
rcText.left = rcGutter.right + data->TextBorder;
#if wxUSE_UXTHEME
wxUxThemeEngine* theme = MenuDrawData::GetUxThemeEngine();
if ( theme )
{ {
// don't draw an edge around the bitmap, if background is white ... POPUPITEMSTATES state;
DWORD menu_bg_color = GetSysColor(COLOR_MENU); if ( stat & wxODDisabled )
if ( GetRValue( menu_bg_color ) >= 0xf0 &&
GetGValue( menu_bg_color ) >= 0xf0 &&
GetBValue( menu_bg_color ) >= 0xf0 )
{ {
draw_bitmap_edge = false; state = (stat & wxODSelected) ? MPI_DISABLEDHOT
: MPI_DISABLED;
} }
} else if ( stat & wxODSelected )
else // edge doesn't look well with default Windows drawing
{ {
draw_bitmap_edge = false; state = MPI_HOT;
}
else
{
state = MPI_NORMAL;
}
wxUxThemeHandle hTheme(GetMenu()->GetWindow(), L"MENU");
if ( theme->IsThemeBackgroundPartiallyTransparent(hTheme,
MENU_POPUPITEM, state) )
{
theme->DrawThemeBackground(hTheme, hdc,
MENU_POPUPBACKGROUND,
0, &rect, NULL);
}
theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPGUTTER,
0, &rcGutter, NULL);
if ( IsSeparator() )
{
rcSeparator.left = rcGutter.right;
theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPSEPARATOR,
0, &rcSeparator, NULL);
return true;
}
theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPITEM,
state, &rcSelection, NULL);
}
else
#endif // wxUSE_UXTHEME
{
if ( IsSeparator() )
{
DrawEdge(hdc, &rcSeparator, EDGE_ETCHED, BF_TOP);
return true;
}
AutoHBRUSH hbr(colBack);
SelectInHDC selBrush(hdc, hbr);
::FillRect(hdc, &rcSelection, hbr);
} }
wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl(); // draw text label
HDC hdc = GetHdcOf(*impl); // using native API because it recognizes '&'
COLORREF colOldText = ::SetTextColor(hdc, colText); COLORREF colOldText = ::SetTextColor(hdc, colText);
COLORREF colOldBack = ::SetBkColor(hdc, colBack); COLORREF colOldBack = ::SetBkColor(hdc, colBack);
// *2, as in wxSYS_EDGE_Y
int margin = GetMarginWidth() + 2 * wxSystemSettings::GetMetric(wxSYS_EDGE_X);
// select the font and draw the text
// ---------------------------------
// determine where to draw and leave space for a check-mark.
// + 1 pixel to separate the edge from the highlight rectangle
int xText = rc.x + margin + 1;
// using native API because it recognizes '&'
if ( IsOwnerDrawn() )
{
int prevMode = SetBkMode(hdc, TRANSPARENT); int prevMode = SetBkMode(hdc, TRANSPARENT);
AutoHBRUSH hbr(colBack);
SelectInHDC selBrush(hdc, hbr);
RECT rectFill;
wxCopyRectToRECT(rc, rectFill);
if ( (stat & wxODSelected) && m_bmpChecked.Ok() && draw_bitmap_edge )
{
// only draw the highlight under the text, not under
// the bitmap or checkmark
rectFill.left = xText;
}
::FillRect(hdc, &rectFill, hbr);
// use default font if no font set
wxFont font;
GetFontToUse(font);
SelectInHDC selFont(hdc, GetHfontOf(font)); SelectInHDC selFont(hdc, GetHfontOf(font));
// item text name without menemonic for calculating size
wxString text = GetName();
SIZE textSize;
::GetTextExtentPoint32(hdc, text.c_str(), text.length(), &textSize);
// item text name with menemonic // item text name with menemonic
wxString text = GetItemLabel().BeforeFirst('\t'); text = GetItemLabel().BeforeFirst('\t');
xText += 3; // separate text from the highlight rectangle
SIZE textRect;
::GetTextExtentPoint32(hdc, text.c_str(), text.length(), &textRect);
int flags = DST_PREFIXTEXT; int flags = DST_PREFIXTEXT;
if ( (stat & wxODDisabled) && !(stat & wxODSelected) ) // themes menu is using specified color for disabled labels
if ( data->MenuLayout() == MenuDrawData::Classic &&
(stat & wxODDisabled) && !(stat & wxODSelected) )
flags |= DSS_DISABLED; flags |= DSS_DISABLED;
if ( (stat & wxODHidePrefix) && !ms_alwaysShowCues ) if ( (stat & wxODHidePrefix) && !data->AlwaysShowCues )
flags |= DSS_HIDEPREFIX; flags |= DSS_HIDEPREFIX;
int x = xText; int x = rcText.left;
int y = rc.y + (rc.GetHeight() - textRect.cy) / 2; int y = rcText.top + (rcText.bottom - rcText.top - textSize.cy) / 2;
int cx = rc.GetWidth() - GetMarginWidth();
int cy = textRect.cy;
::DrawState(hdc, NULL, NULL, (LPARAM)text.wx_str(), ::DrawState(hdc, NULL, NULL, (LPARAM)text.wx_str(),
text.length(), x, y, cx, cy, flags); text.length(), x, y, 0, 0, flags);
// ::SetTextAlign(hdc, TA_RIGHT) doesn't work with DSS_DISABLED or DSS_MONO // ::SetTextAlign(hdc, TA_RIGHT) doesn't work with DSS_DISABLED or DSS_MONO
// as the last parameter in DrawState() (at least with Windows98). So we have // as the last parameter in DrawState() (at least with Windows98). So we have
@@ -642,52 +932,93 @@ bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
wxString accel = GetItemLabel().AfterFirst(wxT('\t')); wxString accel = GetItemLabel().AfterFirst(wxT('\t'));
if ( !accel.empty() ) if ( !accel.empty() )
{ {
SIZE accelRect; SIZE accelSize;
::GetTextExtentPoint32(hdc, accel.c_str(), accel.length(), &accelRect); ::GetTextExtentPoint32(hdc, accel.c_str(), accel.length(), &accelSize);
int flags = DST_TEXT; int flags = DST_TEXT;
if ( (stat & wxODDisabled) && !(stat & wxODSelected) ) // themes menu is using specified color for disabled labels
if ( data->MenuLayout() == MenuDrawData::Classic &&
(stat & wxODDisabled) && !(stat & wxODSelected) )
flags |= DSS_DISABLED; flags |= DSS_DISABLED;
// right align accel string with right edge of menu // right align accel string with right edge of menu
// (offset by the margin width) // (offset by the margin width)
int x = rc.GetWidth() - 16 - accelRect.cx; int x = rcText.right - 16 - accelSize.cx;
int y = rc.y + (rc.GetHeight() - accelRect.cy) / 2; int y = rcText.top + (rcText.bottom - rcText.top - accelSize.cy) / 2;
::DrawState(hdc, NULL, NULL, (LPARAM)accel.wx_str(), ::DrawState(hdc, NULL, NULL, (LPARAM)accel.wx_str(),
accel.length(), x, y, 0, 0, flags); accel.length(), x, y, 0, 0, flags);
} }
::SetBkMode(hdc, prevMode); ::SetBkMode(hdc, prevMode);
::SetBkColor(hdc, colOldBack);
::SetTextColor(hdc, colOldText);
} }
// draw the bitmap // draw the bitmap
// ---------------
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);
if ( IsCheckable() && !m_bmpChecked.Ok() ) if ( IsCheckable() && !m_bmpChecked.Ok() )
{ {
if ( stat & wxODChecked ) 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, // what goes on: DrawFrameControl creates a b/w mask,
// then we copy it to screen to have right colors // then we copy it to screen to have right colors
// first create a monochrome bitmap in a memory DC // first create a monochrome bitmap in a memory DC
HDC hdcMem = CreateCompatibleDC(hdc); HDC hdcMem = ::CreateCompatibleDC(hdc);
HBITMAP hbmpCheck = CreateBitmap(margin, rc.GetHeight(), 1, 1, 0); HBITMAP hbmpCheck = ::CreateBitmap(cx, cy, 1, 1, 0);
SelectObject(hdcMem, hbmpCheck); ::SelectObject(hdcMem, hbmpCheck);
// then draw a check mark into it // then draw a check mark into it
RECT rect = { 0, 0, margin, rc.GetHeight() }; RECT rect = { 0, 0, cx, cy };
if ( rc.GetHeight() > 0 ) if ( rc.GetHeight() > 0 )
{ {
::DrawFrameControl(hdcMem, &rect, DFC_MENU, DFCS_MENUCHECK); ::DrawFrameControl(hdcMem, &rect, DFC_MENU, DFCS_MENUCHECK);
} }
// finally copy it to screen DC and clean up // finally copy it to screen DC and clean up
BitBlt(hdc, rc.x, rc.y, margin, rc.GetHeight(), hdcMem, 0, 0, SRCCOPY); ::BitBlt(hdc, rcImg.left, rcImg.top, cx, cy, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem); ::DeleteDC(hdcMem);
DeleteObject(hbmpCheck); }
} }
} }
else else
@@ -727,28 +1058,13 @@ bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
nBmpHeight = bmp.GetHeight(); nBmpHeight = bmp.GetHeight();
// there should be enough space! // there should be enough space!
wxASSERT((nBmpWidth <= rc.GetWidth()) && (nBmpHeight <= rc.GetHeight())); wxASSERT( nBmpWidth <= imgWidth && nBmpHeight <= (rcImg.bottom - rcImg.top) );
int heightDiff = rc.GetHeight() - nBmpHeight; int x = rcImg.left + (imgWidth - nBmpWidth) / 2;
dc.Blit(rc.x + (margin - nBmpWidth) / 2, int y = rcImg.top + (rcImg.bottom - rcImg.top - nBmpHeight) / 2;
rc.y + heightDiff / 2, dc.Blit(x, y, nBmpWidth, nBmpHeight, &dcMem, 0, 0, wxCOPY, true);
nBmpWidth, nBmpHeight,
&dcMem, 0, 0, wxCOPY, true /* use mask */);
if ( ( stat & wxODSelected ) && !( stat & wxODDisabled ) && draw_bitmap_edge )
{
RECT rectBmp = { rc.GetLeft(), rc.GetTop(),
rc.GetLeft() + margin,
rc.GetTop() + rc.GetHeight() };
SetBkColor(hdc, colBack);
DrawEdge(hdc, &rectBmp, BDR_RAISEDINNER, BF_RECT);
} }
} }
}
::SetTextColor(hdc, colOldText);
::SetBkColor(hdc, colOldBack);
return true; return true;
@@ -758,9 +1074,45 @@ void wxMenuItem::GetFontToUse(wxFont& font) const
{ {
font = GetFont(); font = GetFont();
if ( !font.IsOk() ) if ( !font.IsOk() )
font = ms_systemMenuFont; font = MenuDrawData::Get()->Font;
} }
void wxMenuItem::GetColourToUse(wxODStatus stat, wxColour& colText, wxColour& colBack) const
{
#if wxUSE_UXTHEME
wxUxThemeEngine* theme = MenuDrawData::GetUxThemeEngine();
if ( theme )
{
wxUxThemeHandle hTheme(GetMenu()->GetWindow(), L"MENU");
if ( stat & wxODDisabled)
{
wxRGBToColour(colText, theme->GetThemeSysColor(hTheme, COLOR_GRAYTEXT));
}
else
{
colText = GetTextColour();
if ( !colText.IsOk() )
wxRGBToColour(colText, theme->GetThemeSysColor(hTheme, COLOR_MENUTEXT));
}
if ( stat & wxODSelected )
{
wxRGBToColour(colBack, theme->GetThemeSysColor(hTheme, COLOR_HIGHLIGHT));
}
else
{
colBack = GetBackgroundColour();
if ( !colBack.IsOk() )
wxRGBToColour(colBack, theme->GetThemeSysColor(hTheme, COLOR_MENU));
}
}
else
#endif // wxUSE_UXTHEME
{
wxOwnerDrawn::GetColourToUse(stat, colText, colBack);
}
}
#endif // wxUSE_OWNER_DRAWN #endif // wxUSE_OWNER_DRAWN
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------