From 2a0719818af652700a935eb72b92b1aa7e6d99a8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 17 Oct 2021 01:02:08 +0100 Subject: [PATCH] Update bitmap shown by the buttons in wxMSW on DPI change Recreate wxImageList used for the bitmaps internally with the bitmaps of the size corresponding to the new DPI. This commit is best viewed with --color-moved git option. --- src/msw/anybutton.cpp | 113 +++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 41 deletions(-) diff --git a/src/msw/anybutton.cpp b/src/msw/anybutton.cpp index 2e8c268437..3661da374c 100644 --- a/src/msw/anybutton.cpp +++ b/src/msw/anybutton.cpp @@ -105,9 +105,10 @@ extern wxWindowMSW *wxWindowBeingErased; // From src/msw/window.cpp class wxButtonImageData: public wxObject { public: - explicit wxButtonImageData(const wxSize& bitmapSize) - : m_bitmapSize(bitmapSize) + explicit wxButtonImageData(const wxBitmapBundle& normalBundle) + : m_bitmapSize(normalBundle.GetDefaultSize()) { + m_bitmapBundles[wxAnyButton::State_Normal] = normalBundle; } virtual ~wxButtonImageData() { } @@ -158,7 +159,7 @@ public: virtual wxDirection GetBitmapPosition() const = 0; virtual void SetBitmapPosition(wxDirection dir) = 0; -private: +protected: wxSize m_bitmapSize; wxBitmapBundle m_bitmapBundles[wxAnyButton::State_Max]; @@ -177,9 +178,10 @@ class wxODButtonImageData : public wxButtonImageData { public: wxODButtonImageData(wxAnyButton *btn, const wxBitmapBundle& bitmapBundle) - : wxButtonImageData(bitmapBundle.GetDefaultSize()) + : wxButtonImageData(bitmapBundle) { - SetBitmapBundle(bitmapBundle, wxAnyButton::State_Normal); + SetBitmap(GetBitmapFromBundle(bitmapBundle), + wxAnyButton::State_Normal); #if wxUSE_IMAGE SetBitmap(GetBitmapFromBundle(bitmapBundle).ConvertToDisabled(), wxAnyButton::State_Disabled); @@ -250,44 +252,10 @@ public: // we must be constructed with the size of our images as we need to create // the image list wxXPButtonImageData(wxAnyButton *btn, const wxBitmapBundle& bitmapBundle) - : wxButtonImageData(bitmapBundle.GetDefaultSize()), + : wxButtonImageData(bitmapBundle), m_hwndBtn(GetHwndOf(btn)) { - // initialize all bitmaps except for the disabled one to normal state - const wxBitmap bitmap = bitmapBundle.GetBitmap(wxDefaultSize); - m_iml.Create - ( - bitmap.GetWidth(), - bitmap.GetHeight(), - !bitmap.HasAlpha() /* use mask only if no alpha */, - wxAnyButton::State_Max + 1 /* see "pulse" comment below */ - ); - - for ( int n = 0; n < wxAnyButton::State_Max; n++ ) - { -#if wxUSE_IMAGE - m_iml.Add(n == wxAnyButton::State_Disabled ? bitmap.ConvertToDisabled() - : bitmap); -#else - m_iml.Add(bitmap); -#endif - } - - // In addition to the states supported by wxWidgets such as normal, - // hot, pressed, disabled and focused, we need to add bitmap for - // another state when running under Windows 7 -- the so called "stylus - // hot" state corresponding to PBS_STYLUSHOT constant. While it's - // documented in MSDN as being only used with tablets, it is a lie as - // a focused button actually alternates between the image list elements - // with PBS_DEFAULTED and PBS_STYLUSHOT indices and, in particular, - // just disappears during half of the time if the latter is not set so - // we absolutely must set it. - // - // This also explains why we need to allocate an extra slot when creating - // the image list above, the slot State_Max is used for this one. - m_iml.Add(bitmap); - - m_data.himl = GetHimagelistOf(&m_iml); + InitImageList(); // no margins by default ::SetRectEmpty(&m_data.margin); @@ -296,6 +264,9 @@ public: m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; UpdateImageInfo(); + + // React to DPI changes in the future. + btn->Bind(wxEVT_DPI_CHANGED, &wxXPButtonImageData::OnDPIChanged, this); } virtual wxBitmap GetBitmap(wxAnyButton::State which) const wxOVERRIDE @@ -383,6 +354,52 @@ public: } private: + void InitImageList() + { + const wxBitmap + bitmap = m_bitmapBundles[wxAnyButton::State_Normal].GetBitmap(m_bitmapSize); + + m_iml.Create + ( + bitmap.GetWidth(), + bitmap.GetHeight(), + !bitmap.HasAlpha() /* use mask only if no alpha */, + wxAnyButton::State_Max + 1 /* see "pulse" comment below */ + ); + + m_data.himl = GetHimagelistOf(&m_iml); + + for ( int n = 0; n < wxAnyButton::State_Max; n++ ) + { + wxBitmap stateBitmap = m_bitmapBundles[n].GetBitmap(m_bitmapSize); + if ( !stateBitmap.IsOk() ) + { +#if wxUSE_IMAGE + if ( n == wxAnyButton::State_Disabled ) + stateBitmap = bitmap.ConvertToDisabled(); + else +#endif // wxUSE_IMAGE + stateBitmap = bitmap; + } + + m_iml.Add(bitmap); + } + + // In addition to the states supported by wxWidgets such as normal, + // hot, pressed, disabled and focused, we need to add bitmap for + // another state when running under Windows 7 -- the so called "stylus + // hot" state corresponding to PBS_STYLUSHOT constant. While it's + // documented in MSDN as being only used with tablets, it is a lie as + // a focused button actually alternates between the image list elements + // with PBS_DEFAULTED and PBS_STYLUSHOT indices and, in particular, + // just disappears during half of the time if the latter is not set so + // we absolutely must set it. + // + // This also explains why we need to allocate an extra slot when creating + // the image list above, the slot State_Max is used for this one. + m_iml.Add(bitmap); + } + void UpdateImageInfo() { if ( !::SendMessage(m_hwndBtn, BCM_SETIMAGELIST, 0, (LPARAM)&m_data) ) @@ -391,6 +408,20 @@ private: } } + void OnDPIChanged(wxDPIChangedEvent& event) + { + event.Skip(); + + // We need to recreate the image list using the new size and re-add all + // bitmaps to it. + m_bitmapSize = event.Scale(m_bitmapSize); + + m_iml.Destroy(); + InitImageList(); + + UpdateImageInfo(); + } + // we store image list separately to be able to use convenient wxImageList // methods instead of working with raw HIMAGELIST wxImageList m_iml;