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.
This commit is contained in:
Vadim Zeitlin 2021-10-17 01:02:08 +01:00
parent 4e5d2d97e2
commit 2a0719818a

View File

@ -105,9 +105,10 @@ extern wxWindowMSW *wxWindowBeingErased; // From src/msw/window.cpp
class wxButtonImageData: public wxObject class wxButtonImageData: public wxObject
{ {
public: public:
explicit wxButtonImageData(const wxSize& bitmapSize) explicit wxButtonImageData(const wxBitmapBundle& normalBundle)
: m_bitmapSize(bitmapSize) : m_bitmapSize(normalBundle.GetDefaultSize())
{ {
m_bitmapBundles[wxAnyButton::State_Normal] = normalBundle;
} }
virtual ~wxButtonImageData() { } virtual ~wxButtonImageData() { }
@ -158,7 +159,7 @@ public:
virtual wxDirection GetBitmapPosition() const = 0; virtual wxDirection GetBitmapPosition() const = 0;
virtual void SetBitmapPosition(wxDirection dir) = 0; virtual void SetBitmapPosition(wxDirection dir) = 0;
private: protected:
wxSize m_bitmapSize; wxSize m_bitmapSize;
wxBitmapBundle m_bitmapBundles[wxAnyButton::State_Max]; wxBitmapBundle m_bitmapBundles[wxAnyButton::State_Max];
@ -177,9 +178,10 @@ class wxODButtonImageData : public wxButtonImageData
{ {
public: public:
wxODButtonImageData(wxAnyButton *btn, const wxBitmapBundle& bitmapBundle) 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 #if wxUSE_IMAGE
SetBitmap(GetBitmapFromBundle(bitmapBundle).ConvertToDisabled(), SetBitmap(GetBitmapFromBundle(bitmapBundle).ConvertToDisabled(),
wxAnyButton::State_Disabled); wxAnyButton::State_Disabled);
@ -250,44 +252,10 @@ public:
// we must be constructed with the size of our images as we need to create // we must be constructed with the size of our images as we need to create
// the image list // the image list
wxXPButtonImageData(wxAnyButton *btn, const wxBitmapBundle& bitmapBundle) wxXPButtonImageData(wxAnyButton *btn, const wxBitmapBundle& bitmapBundle)
: wxButtonImageData(bitmapBundle.GetDefaultSize()), : wxButtonImageData(bitmapBundle),
m_hwndBtn(GetHwndOf(btn)) m_hwndBtn(GetHwndOf(btn))
{ {
// initialize all bitmaps except for the disabled one to normal state InitImageList();
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);
// no margins by default // no margins by default
::SetRectEmpty(&m_data.margin); ::SetRectEmpty(&m_data.margin);
@ -296,6 +264,9 @@ public:
m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
UpdateImageInfo(); UpdateImageInfo();
// React to DPI changes in the future.
btn->Bind(wxEVT_DPI_CHANGED, &wxXPButtonImageData::OnDPIChanged, this);
} }
virtual wxBitmap GetBitmap(wxAnyButton::State which) const wxOVERRIDE virtual wxBitmap GetBitmap(wxAnyButton::State which) const wxOVERRIDE
@ -383,6 +354,52 @@ public:
} }
private: 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() void UpdateImageInfo()
{ {
if ( !::SendMessage(m_hwndBtn, BCM_SETIMAGELIST, 0, (LPARAM)&m_data) ) 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 // we store image list separately to be able to use convenient wxImageList
// methods instead of working with raw HIMAGELIST // methods instead of working with raw HIMAGELIST
wxImageList m_iml; wxImageList m_iml;