From fa2242a0a6b1f53b79c65e02340eb94f0915a5f2 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Wed, 4 Sep 2019 20:47:52 +0200 Subject: [PATCH] Reset static sizes when DPI changes --- include/wx/private/window.h | 62 +++++++++++++++++++++++++++++++++++++ include/wx/sizer.h | 10 +----- src/common/sizer.cpp | 15 +++++---- src/common/wincmn.cpp | 10 +++--- src/msw/button.cpp | 28 ++++++++++++----- src/msw/checkbox.cpp | 24 +++++++------- src/msw/cursor.cpp | 22 +++---------- src/msw/radiobut.cpp | 23 +++++++------- 8 files changed, 125 insertions(+), 69 deletions(-) diff --git a/include/wx/private/window.h b/include/wx/private/window.h index 1192fb97b2..18815b3d49 100644 --- a/include/wx/private/window.h +++ b/include/wx/private/window.h @@ -11,6 +11,7 @@ #define _WX_PRIVATE_WINDOW_H_ #include "wx/gdicmn.h" +#include "wx/dynlib.h" namespace wxPrivate { @@ -33,6 +34,67 @@ inline wxSize GetAverageASCIILetterSize(const T& of_what) return s; } +namespace +{ + +inline bool SupportsPerMonitorDPI() +{ + static bool s_checkDPI = +#if defined(__WXMSW__) && wxUSE_DYNLIB_CLASS + // Only check the DPI when GetDpiForWindow is available because the old + // method (GetDeviceCaps) is a lot slower (about 1500 times). + // And when GetDpiForWindow is not available (for example older Windows + // versions), per-monitor DPI (V2) is also not available. + wxLoadedDLL("user32.dll").HasSymbol("GetDpiForWindow"); +#else + false; +#endif + return s_checkDPI; +} + +} + +template +class DpiDependentValue +{ +public: + // Explicit initialization is needed if T is a primitive type. + DpiDependentValue() + : m_value(), m_dpi() + { } + + bool HasChanged(const wxWindowBase* win) + { + if ( win && SupportsPerMonitorDPI() ) + { + const wxSize dpi = win->GetDPI(); + if ( dpi != m_dpi ) + { + m_dpi = dpi; + return true; + } + } + + // Ensure that we return true the first time we're called, + // asuming that the value will always be set to a non-default value. + return m_value == T(); + } + + void SetAtNewDPI(const T& value) + { + m_value = value; + } + + T& Get() + { + return m_value; + } + +private: + T m_value; + wxSize m_dpi; +}; + } // namespace wxPrivate #endif // _WX_PRIVATE_WINDOW_H_ diff --git a/include/wx/sizer.h b/include/wx/sizer.h index 4f34ccd3ed..0d4fdeac09 100644 --- a/include/wx/sizer.h +++ b/include/wx/sizer.h @@ -120,13 +120,7 @@ public: // current DPI, do it once (and cache the result) in another function. #define wxNEEDS_BORDER_IN_PX - // We don't react to dynamic DPI changes, so we can cache the values of - // the border in on-screen pixels after computing it once. This - // could/should change in the future. - if ( !ms_defaultBorderInPx ) - ms_defaultBorderInPx = DoGetDefaultBorderInPx(); - - return ms_defaultBorderInPx; + return DoGetDefaultBorderInPx(); #endif #else return 0; @@ -230,8 +224,6 @@ public: private: #ifdef wxNEEDS_BORDER_IN_PX static int DoGetDefaultBorderInPx(); - - static int ms_defaultBorderInPx; #endif // wxNEEDS_BORDER_IN_PX int m_proportion; diff --git a/src/common/sizer.cpp b/src/common/sizer.cpp index d34ad21eec..d6b270e3e8 100644 --- a/src/common/sizer.cpp +++ b/src/common/sizer.cpp @@ -28,11 +28,13 @@ #include "wx/button.h" #include "wx/statbox.h" #include "wx/toplevel.h" + #include "wx/app.h" #endif // WX_PRECOMP #include "wx/display.h" #include "wx/vector.h" #include "wx/listimpl.cpp" +#include "wx/private/window.h" //--------------------------------------------------------------------------- @@ -90,8 +92,6 @@ WX_DEFINE_EXPORTED_LIST( wxSizerItemList ) #ifdef wxNEEDS_BORDER_IN_PX -int wxSizerFlags::ms_defaultBorderInPx = 0; - /* static */ int wxSizerFlags::DoGetDefaultBorderInPx() { @@ -103,11 +103,14 @@ int wxSizerFlags::DoGetDefaultBorderInPx() // between related and unrelated controls, as explained at the above URL, // but we don't have a way to specify this in our API currently. // - // We also have to use the DPI for the primary monitor here as we don't - // have any associated window, so this is wrong on systems using multiple - // monitors with different resolutions too -- but, again, without changes + // We also have to use the DPI for the monitor showing the top window here + // as we don't have any associated window -- but, again, without changes // in the API, there is nothing we can do about this. - return wxWindow::FromDIP(5, NULL); + const wxWindow* const win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + static wxPrivate::DpiDependentValue s_defaultBorderInPx; + if ( s_defaultBorderInPx.HasChanged(win) ) + s_defaultBorderInPx.SetAtNewDPI(wxWindow::FromDIP(5, win)); + return s_defaultBorderInPx.Get(); } #endif // wxNEEDS_BORDER_IN_PX diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index bc8a88139a..9d0ef0a1b8 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -2921,7 +2921,7 @@ wxWindowBase::ToDIP(const wxSize& sz, const wxWindowBase* w) // using them. wxSize wxWindowBase::GetDlgUnitBase() const { - const wxWindowBase * const parent = wxGetTopLevelParent((wxWindow*)this); + const wxWindowBase* const parent = wxGetTopLevelParent((wxWindow*)this); wxCHECK_MSG( parent, wxDefaultSize, wxS("Must have TLW parent") ); @@ -2929,10 +2929,10 @@ wxSize wxWindowBase::GetDlgUnitBase() const { // Default GUI font is used. This is the most common case, so // cache the results. - static wxSize s_defFontSize; - if ( s_defFontSize.x == 0 ) - s_defFontSize = wxPrivate::GetAverageASCIILetterSize(*parent); - return s_defFontSize; + static wxPrivate::DpiDependentValue s_defFontSize; + if ( s_defFontSize.HasChanged(parent) ) + s_defFontSize.SetAtNewDPI(wxPrivate::GetAverageASCIILetterSize(*parent)); + return s_defFontSize.Get(); } else { diff --git a/src/msw/button.cpp b/src/msw/button.cpp index c9df03a590..786bdc143d 100644 --- a/src/msw/button.cpp +++ b/src/msw/button.cpp @@ -162,12 +162,25 @@ WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const /* static */ wxSize wxButtonBase::GetDefaultSize() { - static wxSize s_sizeBtn; + wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; - if ( s_sizeBtn.x == 0 ) + static wxPrivate::DpiDependentValue s_sizeBtn; + + if ( s_sizeBtn.HasChanged(win) ) { - wxScreenDC dc; - dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + wxSize base; + if ( win ) + { + wxClientDC dc(win); + dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + base = wxPrivate::GetAverageASCIILetterSize(dc); + } + else + { + wxScreenDC dc; + dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + base = wxPrivate::GetAverageASCIILetterSize(dc); + } // The size of a standard button in the dialog units is 50x14, // translate this to pixels. @@ -180,12 +193,11 @@ wxSize wxButtonBase::GetDefaultSize() // // NB: wxMulDivInt32() is used, because it correctly rounds the result - const wxSize base = wxPrivate::GetAverageASCIILetterSize(dc); - s_sizeBtn.x = wxMulDivInt32(50, base.x, 4); - s_sizeBtn.y = wxMulDivInt32(14, base.y, 8); + s_sizeBtn.SetAtNewDPI(wxSize(wxMulDivInt32(50, base.x, 4), + wxMulDivInt32(14, base.y, 8))); } - return s_sizeBtn; + return s_sizeBtn.Get(); } // ---------------------------------------------------------------------------- diff --git a/src/msw/checkbox.cpp b/src/msw/checkbox.cpp index fa8ca811ce..5414ceca21 100644 --- a/src/msw/checkbox.cpp +++ b/src/msw/checkbox.cpp @@ -28,15 +28,14 @@ #include "wx/checkbox.h" #ifndef WX_PRECOMP - #include "wx/brush.h" #include "wx/dcclient.h" - #include "wx/dcscreen.h" #include "wx/settings.h" #endif #include "wx/renderer.h" #include "wx/msw/uxtheme.h" #include "wx/msw/private/button.h" +#include "wx/private/window.h" #include "wx/msw/missing.h" // ============================================================================ @@ -98,16 +97,17 @@ WXDWORD wxCheckBox::MSWGetStyle(long style, WXDWORD *exstyle) const wxSize wxCheckBox::DoGetBestClientSize() const { - static int s_checkSize = 0; + static wxPrivate::DpiDependentValue s_checkSize; - if ( !s_checkSize ) + if ( s_checkSize.HasChanged(this) ) { - wxScreenDC dc; + wxClientDC dc(const_cast(this)); dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - s_checkSize = dc.GetCharHeight(); + s_checkSize.SetAtNewDPI(dc.GetCharHeight()); } + wxCoord& checkSize = s_checkSize.Get(); wxString str = wxGetWindowText(GetHWND()); int wCheckbox, hCheckbox; @@ -116,7 +116,7 @@ wxSize wxCheckBox::DoGetBestClientSize() const wxClientDC dc(const_cast(this)); dc.SetFont(GetFont()); dc.GetMultiLineTextExtent(GetLabelText(str), &wCheckbox, &hCheckbox); - wCheckbox += s_checkSize + GetCharWidth(); + wCheckbox += checkSize + GetCharWidth(); if ( ::GetWindowLong(GetHwnd(), GWL_STYLE) & BS_MULTILINE ) { @@ -128,16 +128,16 @@ wxSize wxCheckBox::DoGetBestClientSize() const // label appears on 3 lines, not 2, under Windows 2003 using // classic look and feel (although it works fine under Windows 7, // with or without themes). - wCheckbox += s_checkSize; + wCheckbox += checkSize; } - if ( hCheckbox < s_checkSize ) - hCheckbox = s_checkSize; + if ( hCheckbox < checkSize ) + hCheckbox = checkSize; } else { - wCheckbox = s_checkSize; - hCheckbox = s_checkSize; + wCheckbox = checkSize; + hCheckbox = checkSize; } return wxSize(wCheckbox, hCheckbox); diff --git a/src/msw/cursor.cpp b/src/msw/cursor.cpp index d051acc52f..d57f980ea5 100644 --- a/src/msw/cursor.cpp +++ b/src/msw/cursor.cpp @@ -63,9 +63,6 @@ public: private: bool m_destroyCursor; - - // standard cursor size, computed on first use - static wxSize ms_sizeStd; }; // ---------------------------------------------------------------------------- @@ -110,28 +107,17 @@ public: // wxCursorRefData // ---------------------------------------------------------------------------- -wxSize wxCursorRefData::ms_sizeStd; wxCoord wxCursorRefData::GetStandardWidth() { - if ( !ms_sizeStd.x ) - { - wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; - ms_sizeStd.x = wxSystemSettings::GetMetric(wxSYS_CURSOR_X, win); - } - - return ms_sizeStd.x; + const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + return wxSystemSettings::GetMetric(wxSYS_CURSOR_X, win); } wxCoord wxCursorRefData::GetStandardHeight() { - if ( !ms_sizeStd.y ) - { - wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; - ms_sizeStd.y = wxSystemSettings::GetMetric(wxSYS_CURSOR_Y, win); - } - - return ms_sizeStd.y; + const wxWindow* win = wxTheApp ? wxTheApp->GetTopWindow() : NULL; + return wxSystemSettings::GetMetric(wxSYS_CURSOR_Y, win); } wxCursorRefData::wxCursorRefData(HCURSOR hcursor, bool destroy) diff --git a/src/msw/radiobut.cpp b/src/msw/radiobut.cpp index 84a16ddbfa..ef3d7ec15f 100644 --- a/src/msw/radiobut.cpp +++ b/src/msw/radiobut.cpp @@ -29,11 +29,11 @@ #ifndef WX_PRECOMP #include "wx/settings.h" - #include "wx/dcscreen.h" - #include "wx/toplevel.h" + #include "wx/dcclient.h" #endif #include "wx/msw/private.h" +#include "wx/private/window.h" #include "wx/renderer.h" #include "wx/msw/uxtheme.h" @@ -238,31 +238,32 @@ bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) wxSize wxRadioButton::DoGetBestSize() const { - static int s_radioSize = 0; + static wxPrivate::DpiDependentValue s_radioSize; - if ( !s_radioSize ) + if ( s_radioSize.HasChanged(this) ) { - wxScreenDC dc; + wxClientDC dc(const_cast(this)); dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - s_radioSize = dc.GetCharHeight(); + s_radioSize.SetAtNewDPI(dc.GetCharHeight()); } + wxCoord& radioSize = s_radioSize.Get(); wxString str = GetLabel(); int wRadio, hRadio; if ( !str.empty() ) { GetTextExtent(GetLabelText(str), &wRadio, &hRadio); - wRadio += s_radioSize + GetCharWidth(); + wRadio += radioSize + GetCharWidth(); - if ( hRadio < s_radioSize ) - hRadio = s_radioSize; + if ( hRadio < radioSize ) + hRadio = radioSize; } else { - wRadio = s_radioSize; - hRadio = s_radioSize; + wRadio = radioSize; + hRadio = radioSize; } return wxSize(wRadio, hRadio);