From 27be2ed641d5f6c70278599730f714f53faf15d8 Mon Sep 17 00:00:00 2001 From: Alexander Koshelev Date: Fri, 28 Jan 2022 16:28:18 +0300 Subject: [PATCH] wxBitmapBundle for wxMenuItem on MSW --- include/wx/msw/menu.h | 16 ++++++++++ include/wx/msw/menuitem.h | 28 +++++++++-------- samples/menu/menu.cpp | 2 +- src/msw/menu.cpp | 66 ++++++++++++++++++++++++++++++--------- src/msw/menuitem.cpp | 58 +++++++++++++++++++++++++--------- src/msw/window.cpp | 2 ++ 6 files changed, 130 insertions(+), 42 deletions(-) diff --git a/include/wx/msw/menu.h b/include/wx/msw/menu.h index 31d104bccf..c372d7490f 100644 --- a/include/wx/msw/menu.h +++ b/include/wx/msw/menu.h @@ -69,6 +69,12 @@ public: // containing this position. bool MSWGetRadioGroupRange(int pos, int *start, int *end) const; +#if wxUSE_MENUBAR + virtual void Attach(wxMenuBarBase *menubar) wxOVERRIDE; +#endif + + void SetupBitmaps(); + #if wxUSE_ACCEL // called by wxMenuBar to build its accel table from the accels of all menus bool HasAccels() const { return !m_accels.empty(); } @@ -220,6 +226,16 @@ protected: // common part of all ctors void Init(); + void SetupBitmaps(); + + void OnDPIChanged(wxDPIChangedEvent& event) + { + // need to reset bitmaps + SetupBitmaps(); + + event.Skip(); + } + WXHMENU m_hMenu; // Return the MSW position for a wxMenu which is sometimes different from diff --git a/include/wx/msw/menuitem.h b/include/wx/msw/menuitem.h index 3b4fb07e98..64b1b75cd5 100644 --- a/include/wx/msw/menuitem.h +++ b/include/wx/msw/menuitem.h @@ -15,7 +15,7 @@ // headers // ---------------------------------------------------------------------------- -#include "wx/bitmap.h" +#include "wx/bmpbndl.h" #if wxUSE_OWNER_DRAWN #include "wx/ownerdrw.h" @@ -73,30 +73,30 @@ public: ); #endif - void SetBitmaps(const wxBitmap& bmpChecked, - const wxBitmap& bmpUnchecked = wxNullBitmap) + void SetBitmaps(const wxBitmapBundle& bmpChecked, + const wxBitmapBundle& bmpUnchecked = wxNullBitmap) { DoSetBitmap(bmpChecked, true); DoSetBitmap(bmpUnchecked, false); } - void SetBitmap(const wxBitmap& bmp, bool bChecked = true) + void SetBitmap(const wxBitmapBundle& bmp, bool bChecked = true) { DoSetBitmap(bmp, bChecked); } - const wxBitmap& GetBitmap(bool bChecked = true) const - { return (bChecked ? m_bmpChecked : m_bmpUnchecked); } + void SetupBitmaps(); + + wxBitmap GetBitmap(bool bChecked = true) const; #if wxUSE_OWNER_DRAWN - void SetDisabledBitmap(const wxBitmap& bmpDisabled) + void SetDisabledBitmap(const wxBitmapBundle& bmpDisabled) { m_bmpDisabled = bmpDisabled; SetOwnerDrawn(true); } - const wxBitmap& GetDisabledBitmap() const - { return m_bmpDisabled; } + wxBitmap GetDisabledBitmap() const; int MeasureAccelWidth() const; @@ -128,12 +128,14 @@ private: WXHBITMAP GetHBitmapForMenu(BitmapKind kind) const; // helper function to set/change the bitmap - void DoSetBitmap(const wxBitmap& bmp, bool bChecked); + void DoSetBitmap(const wxBitmapBundle& bmp, bool bChecked); private: // common part of all ctors void Init(); + wxBitmap GetBitmapFromBundle(const wxBitmapBundle& bundle) const; + // Return the item position in the menu containing it. // // Returns -1 if the item is not attached to a menu or if we can't find its @@ -141,10 +143,10 @@ private: int MSGetMenuItemPos() const; // item bitmaps - wxBitmap m_bmpChecked, // bitmap to put near the item - m_bmpUnchecked; // (checked is used also for 'uncheckable' items) + wxBitmapBundle m_bmpChecked, // bitmap to put near the item + m_bmpUnchecked; // (checked is used also for 'uncheckable' items) #if wxUSE_OWNER_DRAWN - wxBitmap m_bmpDisabled; + wxBitmapBundle m_bmpDisabled; #endif // wxUSE_OWNER_DRAWN // Give wxMenu access to our MSWMustUseOwnerDrawn() and GetHBitmapForMenu(). diff --git a/samples/menu/menu.cpp b/samples/menu/menu.cpp index 6387705fc4..51119b2fae 100644 --- a/samples/menu/menu.cpp +++ b/samples/menu/menu.cpp @@ -559,7 +559,7 @@ MyFrame::MyFrame() #if USE_LOG_WINDOW wxMenuItem *item = new wxMenuItem(fileMenu, Menu_File_ClearLog, "Clear &log\tCtrl-L"); - item->SetBitmap(copy_xpm); + item->SetBitmap(wxBitmap(copy_xpm)); fileMenu->Append(item); fileMenu->AppendSeparator(); #endif // USE_LOG_WINDOW diff --git a/src/msw/menu.cpp b/src/msw/menu.cpp index 4ac1468572..ce99d0f685 100644 --- a/src/msw/menu.cpp +++ b/src/msw/menu.cpp @@ -305,6 +305,38 @@ bool wxMenu::MSWGetRadioGroupRange(int pos, int *start, int *end) const return m_radioData && m_radioData->GetGroupRange(pos, start, end); } +#if wxUSE_MENUBAR +void wxMenu::Attach(wxMenuBarBase* menubar) +{ + wxMenuBase::Attach(menubar); + + if (menubar->IsAttached()) + { + // menubar is already attached, we need to call SetupBitmaps + SetupBitmaps(); + } +} +#endif + +void wxMenu::SetupBitmaps() +{ + for ( wxMenuItemList::compatibility_iterator node = m_items.GetFirst(); + node; + node = node->GetNext() ) + { + wxMenuItem *item = node->GetData(); + if ( item->IsSubMenu() ) + { + item->GetSubMenu()->SetupBitmaps(); + } + + if ( !item->IsSeparator() ) + { + item->SetupBitmaps(); + } + } +} + // append a new item or submenu to the menu bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) { @@ -413,20 +445,6 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) WinStruct mii; mii.fMask = MIIM_STRING | MIIM_DATA; - // don't set hbmpItem for the checkable items as it would - // be used for both checked and unchecked state - if ( pItem->IsCheckable() ) - { - mii.fMask |= MIIM_CHECKMARKS; - mii.hbmpChecked = pItem->GetHBitmapForMenu(wxMenuItem::Checked); - mii.hbmpUnchecked = pItem->GetHBitmapForMenu(wxMenuItem::Unchecked); - } - else if ( pItem->GetBitmap().IsOk() ) - { - mii.fMask |= MIIM_BITMAP; - mii.hbmpItem = pItem->GetHBitmapForMenu(wxMenuItem::Normal); - } - mii.cch = itemText.length(); mii.dwTypeData = wxMSW_CONV_LPTSTR(itemText); @@ -593,6 +611,14 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos) // if we're already attached to the menubar, we must update it if ( IsAttached() && GetMenuBar()->IsAttached() ) { + if ( pItem->IsSubMenu() ) + { + pItem->GetSubMenu()->SetupBitmaps(); + } + if ( !pItem->IsSeparator() ) + { + pItem->SetupBitmaps(); + } GetMenuBar()->Refresh(); } @@ -1233,6 +1259,14 @@ void wxMenuBar::RebuildAccelTable() #endif // wxUSE_ACCEL +void wxMenuBar::SetupBitmaps() +{ + for ( wxMenuList::const_iterator it = m_menus.begin(); it != m_menus.end(); ++it ) + { + (*it)->SetupBitmaps(); + } +} + void wxMenuBar::Attach(wxFrame *frame) { wxMenuBarBase::Attach(frame); @@ -1240,6 +1274,10 @@ void wxMenuBar::Attach(wxFrame *frame) #if wxUSE_ACCEL RebuildAccelTable(); #endif // wxUSE_ACCEL + + SetupBitmaps(); + + frame->Bind(wxEVT_DPI_CHANGED, &wxMenuBar::OnDPIChanged, this); } void wxMenuBar::Detach() diff --git a/src/msw/menuitem.cpp b/src/msw/menuitem.cpp index 9bb397d472..38e249d743 100644 --- a/src/msw/menuitem.cpp +++ b/src/msw/menuitem.cpp @@ -688,25 +688,50 @@ void wxMenuItem::SetItemLabel(const wxString& txt) } } -void wxMenuItem::DoSetBitmap(const wxBitmap& bmpNew, bool bChecked) +wxBitmap wxMenuItem::GetBitmapFromBundle(const wxBitmapBundle& bundle) const { - wxBitmap& bmp = bChecked ? m_bmpChecked : m_bmpUnchecked; - if ( bmp.IsSameAs(bmpNew) ) - return; + if (bundle.IsOk()) + { + if (m_parentMenu && m_parentMenu->GetWindow()) + { + return bundle.GetBitmapFor(m_parentMenu->GetWindow()); + } + else + { + return bundle.GetBitmap(wxDefaultSize); + } + } + return wxNullBitmap; +} +wxBitmap wxMenuItem::GetBitmap(bool bChecked) const +{ + wxBitmap bmp = GetBitmapFromBundle(bChecked ? m_bmpChecked : m_bmpUnchecked); #if wxUSE_IMAGE - if ( !bmpNew.HasAlpha() && wxGetWinVersion() >= wxWinVersion_Vista) + if ( bmp.IsOk() && !bmp.HasAlpha() && wxGetWinVersion() >= wxWinVersion_Vista) { // we must use PARGB DIB for the menu bitmaps so ensure that we do - wxImage img(bmpNew.ConvertToImage()); + wxImage img(bmp.ConvertToImage()); img.InitAlpha(); bmp = wxBitmap(img); } - else #endif // wxUSE_IMAGE - { - bmp = bmpNew; - } + return bmp; +} + +#if wxUSE_OWNER_DRAWN +wxBitmap wxMenuItem::GetDisabledBitmap() const +{ + return GetBitmapFromBundle(m_bmpDisabled); +} +#endif + +void wxMenuItem::DoSetBitmap(const wxBitmapBundle& bmpNew, bool bChecked) +{ + wxBitmapBundle& bmp = bChecked ? m_bmpChecked : m_bmpUnchecked; + if ( bmp.IsSameAs(bmpNew) ) + return; + bmp = bmpNew; #if wxUSE_OWNER_DRAWN // already marked as owner-drawn, cannot be reverted @@ -738,7 +763,10 @@ void wxMenuItem::DoSetBitmap(const wxBitmap& bmpNew, bool bChecked) return; } #endif // wxUSE_OWNER_DRAWN +} +void wxMenuItem::SetupBitmaps() +{ const int itemPos = MSGetMenuItemPos(); if ( itemPos == -1 ) { @@ -855,8 +883,10 @@ bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height) { // 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 widthBmp = wxMax(m_bmpChecked.GetWidth(), m_bmpUnchecked.GetWidth()); + wxBitmap bmpChecked = GetBitmap(true); + wxBitmap bmpUnchecked = GetBitmap(false); + size_t heightBmp = wxMax(bmpChecked.GetLogicalHeight(), bmpUnchecked.GetLogicalHeight()); + size_t widthBmp = wxMax(bmpChecked.GetLogicalWidth(), bmpUnchecked.GetLogicalWidth()); if ( IsOwnerDrawn() ) { @@ -1113,8 +1143,8 @@ bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc, dcMem.SelectObjectAsSource(bmp); // center bitmap - int nBmpWidth = bmp.GetWidth(), - nBmpHeight = bmp.GetHeight(); + int nBmpWidth = bmp.GetLogicalWidth(), + nBmpHeight = bmp.GetLogicalHeight(); int x = rcImg.left + (imgWidth - nBmpWidth) / 2; int y = rcImg.top + (rcImg.bottom - rcImg.top - nBmpHeight) / 2; diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 947f63f4bf..4e1ec7d527 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -2367,6 +2367,8 @@ static void wxYieldForCommandsOnly() bool wxWindowMSW::DoPopupMenu(wxMenu *menu, int x, int y) { + menu->SetupBitmaps(); + wxPoint pt; if ( x == wxDefaultCoord && y == wxDefaultCoord ) {