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:
@@ -115,6 +115,7 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void GetFontToUse(wxFont& font) const;
|
||||
virtual void GetColourToUse(wxODStatus stat, wxColour& colText, wxColour& colBack) const;
|
||||
|
||||
#endif // wxUSE_OWNER_DRAWN
|
||||
|
||||
@@ -140,11 +141,6 @@ private:
|
||||
wxBitmap m_bmpChecked, // bitmap to put near the item
|
||||
m_bmpUnchecked, // (checked is used also for 'uncheckable' items)
|
||||
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
|
||||
|
||||
DECLARE_DYNAMIC_CLASS_NO_COPY(wxMenuItem)
|
||||
|
@@ -69,6 +69,8 @@ enum
|
||||
Menu_Bitmap, Menu_Bitmap2,
|
||||
Menu_Submenu, Menu_Sub1, Menu_Sub2, Menu_Sub3,
|
||||
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_Listbox, Control_Listbox2
|
||||
};
|
||||
@@ -198,9 +200,62 @@ void OwnerDrawnFrame::InitMenu()
|
||||
wxITEM_NORMAL);
|
||||
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;
|
||||
|
||||
menu_bar->Append(file_menu, wxT("&File"));
|
||||
menu_bar->Append(drawn_menu, wxT("&Drawn"));
|
||||
menu_bar->Append(native_menu, wxT("&Native"));
|
||||
SetMenuBar(menu_bar);
|
||||
}
|
||||
|
||||
|
@@ -86,8 +86,17 @@ void wxOwnerDrawnBase::GetColourToUse(wxODStatus stat, wxColour& colText, wxColo
|
||||
else
|
||||
{
|
||||
// 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
|
||||
: wxSystemSettings::GetColour(wxSYS_COLOUR_MENUTEXT);
|
||||
}
|
||||
|
||||
colBack = m_colBack.Ok() ? m_colBack
|
||||
: wxSystemSettings::GetColour(wxSYS_COLOUR_MENU);
|
||||
}
|
||||
|
@@ -112,7 +112,8 @@ void SetDefaultMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
|
||||
// make the given menu item owner-drawn
|
||||
void SetOwnerDrawnMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
|
||||
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__
|
||||
MENUITEMINFO mii;
|
||||
@@ -122,7 +123,10 @@ void SetOwnerDrawnMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
|
||||
mii.fType = MFT_OWNERDRAW;
|
||||
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"));
|
||||
}
|
||||
@@ -496,7 +500,7 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
|
||||
// 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
|
||||
// other items already are.
|
||||
if ( m_ownerDrawn && !pItem->IsSeparator() )
|
||||
if ( m_ownerDrawn )
|
||||
pItem->SetOwnerDrawn(true);
|
||||
#endif // wxUSE_OWNER_DRAWN
|
||||
|
||||
@@ -506,7 +510,7 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
|
||||
{
|
||||
#ifndef __DMC__
|
||||
|
||||
if ( !m_ownerDrawn )
|
||||
if ( !m_ownerDrawn && !pItem->IsSeparator() )
|
||||
{
|
||||
// MIIM_BITMAP only works under WinME/2000+ so we always use owner
|
||||
// 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
|
||||
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();
|
||||
while (node)
|
||||
{
|
||||
wxMenuItem* item = node->GetData();
|
||||
|
||||
if ( !item->IsSeparator() )
|
||||
{
|
||||
if ( !item->IsOwnerDrawn() )
|
||||
if ( !item->IsOwnerDrawn())
|
||||
{
|
||||
item->SetOwnerDrawn(true);
|
||||
SetOwnerDrawnMenuItem(GetHmenu(), item->GetMSWId(),
|
||||
reinterpret_cast<ULONG_PTR>(item));
|
||||
}
|
||||
item->SetMarginWidth(m_maxBitmapWidth);
|
||||
SetOwnerDrawnMenuItem(GetHmenu(), pos,
|
||||
reinterpret_cast<ULONG_PTR>(item), TRUE);
|
||||
}
|
||||
|
||||
item->SetMarginWidth(m_maxBitmapWidth);
|
||||
|
||||
node = node->GetNext();
|
||||
pos++;
|
||||
}
|
||||
|
||||
// set menu as ownerdrawn
|
||||
|
@@ -52,6 +52,10 @@
|
||||
UINT GetMenuState(HMENU hMenu, UINT id, UINT flags) ;
|
||||
#endif
|
||||
|
||||
#if wxUSE_UXTHEME
|
||||
#include "wx/msw/uxtheme.h"
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// macro
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -76,6 +80,53 @@ UINT GetMenuState(HMENU hMenu, UINT id, UINT flags) ;
|
||||
#define DSS_HIDEPREFIX 0x0200
|
||||
#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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -131,11 +182,208 @@ IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
|
||||
|
||||
#if wxUSE_OWNER_DRAWN
|
||||
|
||||
// these static variables are from the wxMenuItem object for cache
|
||||
// system settings returned by the Win32 API's SystemParametersInfo() call
|
||||
wxFont wxMenuItem::ms_systemMenuFont;
|
||||
size_t wxMenuItem::ms_systemMenuHeight = 0;
|
||||
bool wxMenuItem::ms_alwaysShowCues = false;
|
||||
namespace
|
||||
{
|
||||
|
||||
// helper class to keep information about metrics and other stuff
|
||||
// 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
|
||||
|
||||
@@ -175,24 +423,6 @@ void wxMenuItem::Init()
|
||||
|
||||
#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.
|
||||
// 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
|
||||
@@ -460,21 +690,27 @@ wxString wxMenuItem::GetName() const
|
||||
|
||||
bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
|
||||
{
|
||||
const MenuDrawData* data = MenuDrawData::Get();
|
||||
|
||||
if ( IsOwnerDrawn() )
|
||||
{
|
||||
*width = data->ItemMargin.left + data->ItemMargin.right;
|
||||
*height = data->ItemMargin.top + data->ItemMargin.bottom;
|
||||
|
||||
wxString str = GetName();
|
||||
|
||||
// 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() )
|
||||
if ( IsSeparator() )
|
||||
{
|
||||
str.Pad(str.length()%8);
|
||||
str += accel;
|
||||
*width += data->SeparatorSize.cx
|
||||
+ 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;
|
||||
wxFont font;
|
||||
GetFontToUse(font);
|
||||
@@ -482,8 +718,13 @@ bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
|
||||
|
||||
wxCoord w, h;
|
||||
dc.GetTextExtent(str, &w, &h);
|
||||
*width = w;
|
||||
|
||||
*width = w + data->TextBorder + 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// increase size to accommodate bigger bitmaps if necessary
|
||||
if (m_bmpChecked.Ok())
|
||||
// bitmap
|
||||
|
||||
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?
|
||||
size_t adjustedHeight = m_bmpChecked.GetHeight();
|
||||
if ( *height < adjustedHeight )
|
||||
*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;
|
||||
if ( *height < heightBmp )
|
||||
*height = heightBmp;
|
||||
}
|
||||
|
||||
// make sure that this item is at least as tall as the system menu height
|
||||
if ( *height < ms_systemMenuHeight )
|
||||
*height = ms_systemMenuHeight;
|
||||
const size_t menuHeight = data->CheckMargin.top + data->CheckMargin.bottom
|
||||
+ data->CheckSize.cy;
|
||||
if (*height < menuHeight)
|
||||
*height = menuHeight;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -535,106 +782,149 @@ bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
|
||||
bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
|
||||
wxODAction WXUNUSED(act), wxODStatus stat)
|
||||
{
|
||||
const MenuDrawData* data = MenuDrawData::Get();
|
||||
|
||||
// this flag determines whether or not an edge will
|
||||
// be drawn around the bitmap. In most "windows classic"
|
||||
// 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)
|
||||
wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
|
||||
HDC hdc = GetHdcOf(*impl);
|
||||
|
||||
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;
|
||||
GetColourToUse(stat, colText1, colBack1);
|
||||
|
||||
DWORD colText = wxColourToPalRGB(colText1);
|
||||
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 ...
|
||||
DWORD menu_bg_color = GetSysColor(COLOR_MENU);
|
||||
if ( GetRValue( menu_bg_color ) >= 0xf0 &&
|
||||
GetGValue( menu_bg_color ) >= 0xf0 &&
|
||||
GetBValue( menu_bg_color ) >= 0xf0 )
|
||||
POPUPITEMSTATES state;
|
||||
if ( stat & wxODDisabled )
|
||||
{
|
||||
draw_bitmap_edge = false;
|
||||
state = (stat & wxODSelected) ? MPI_DISABLEDHOT
|
||||
: MPI_DISABLED;
|
||||
}
|
||||
}
|
||||
else // edge doesn't look well with default Windows drawing
|
||||
else if ( stat & wxODSelected )
|
||||
{
|
||||
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();
|
||||
HDC hdc = GetHdcOf(*impl);
|
||||
// draw text label
|
||||
// using native API because it recognizes '&'
|
||||
|
||||
COLORREF colOldText = ::SetTextColor(hdc, colText);
|
||||
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);
|
||||
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));
|
||||
|
||||
|
||||
// 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
|
||||
wxString text = GetItemLabel().BeforeFirst('\t');
|
||||
|
||||
xText += 3; // separate text from the highlight rectangle
|
||||
|
||||
SIZE textRect;
|
||||
::GetTextExtentPoint32(hdc, text.c_str(), text.length(), &textRect);
|
||||
text = GetItemLabel().BeforeFirst('\t');
|
||||
|
||||
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;
|
||||
|
||||
if ( (stat & wxODHidePrefix) && !ms_alwaysShowCues )
|
||||
if ( (stat & wxODHidePrefix) && !data->AlwaysShowCues )
|
||||
flags |= DSS_HIDEPREFIX;
|
||||
|
||||
int x = xText;
|
||||
int y = rc.y + (rc.GetHeight() - textRect.cy) / 2;
|
||||
int cx = rc.GetWidth() - GetMarginWidth();
|
||||
int cy = textRect.cy;
|
||||
int x = rcText.left;
|
||||
int y = rcText.top + (rcText.bottom - rcText.top - textSize.cy) / 2;
|
||||
|
||||
::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
|
||||
// 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'));
|
||||
if ( !accel.empty() )
|
||||
{
|
||||
SIZE accelRect;
|
||||
::GetTextExtentPoint32(hdc, accel.c_str(), accel.length(), &accelRect);
|
||||
SIZE accelSize;
|
||||
::GetTextExtentPoint32(hdc, accel.c_str(), accel.length(), &accelSize);
|
||||
|
||||
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;
|
||||
|
||||
// right align accel string with right edge of menu
|
||||
// (offset by the margin width)
|
||||
|
||||
int x = rc.GetWidth() - 16 - accelRect.cx;
|
||||
int y = rc.y + (rc.GetHeight() - accelRect.cy) / 2;
|
||||
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(),
|
||||
accel.length(), x, y, 0, 0, flags);
|
||||
}
|
||||
|
||||
::SetBkMode(hdc, prevMode);
|
||||
::SetBkColor(hdc, colOldBack);
|
||||
::SetTextColor(hdc, colOldText);
|
||||
}
|
||||
|
||||
|
||||
// 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 ( 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(margin, rc.GetHeight(), 1, 1, 0);
|
||||
SelectObject(hdcMem, hbmpCheck);
|
||||
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, margin, rc.GetHeight() };
|
||||
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, 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);
|
||||
DeleteObject(hbmpCheck);
|
||||
::DeleteDC(hdcMem);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -727,28 +1058,13 @@ bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
|
||||
nBmpHeight = bmp.GetHeight();
|
||||
|
||||
// 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;
|
||||
dc.Blit(rc.x + (margin - nBmpWidth) / 2,
|
||||
rc.y + heightDiff / 2,
|
||||
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);
|
||||
int x = rcImg.left + (imgWidth - nBmpWidth) / 2;
|
||||
int y = rcImg.top + (rcImg.bottom - rcImg.top - nBmpHeight) / 2;
|
||||
dc.Blit(x, y, nBmpWidth, nBmpHeight, &dcMem, 0, 0, wxCOPY, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::SetTextColor(hdc, colOldText);
|
||||
::SetBkColor(hdc, colOldBack);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -758,9 +1074,45 @@ void wxMenuItem::GetFontToUse(wxFont& font) const
|
||||
{
|
||||
font = GetFont();
|
||||
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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
Reference in New Issue
Block a user