From 421416696fc68940a1928e15c5fcdae33662bbe1 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 13 Jan 2022 18:46:54 +0000 Subject: [PATCH 1/2] Add wxWindow::FromPhys() and ToPhys() functions Provide these functions for consistency with the existing FromDIP() and ToDIP(). We also can use ceil() (rather than round() used by wxSize operators) in FromPhys() to ensure that we never truncate contents of a physical bitmap, which allows to replace the existing wxStaticBitmap code with just a call to this function. --- include/wx/window.h | 51 ++++++++++++++++++++ interface/wx/window.h | 97 +++++++++++++++++++++++++++++++++++++++ src/common/statbmpcmn.cpp | 17 +------ src/common/wincmn.cpp | 76 ++++++++++++++++++++++++++++-- 4 files changed, 222 insertions(+), 19 deletions(-) diff --git a/include/wx/window.h b/include/wx/window.h index 48efebc63d..2fafa644c4 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -981,6 +981,57 @@ public: // wxWidgets public API. virtual void WXAdjustFontToOwnPPI(wxFont& WXUNUSED(font)) const { } + // All pixel coordinates used in wx API are in logical pixels, which + // are the same as physical screen pixels under MSW, but same as DIPs + // (see below) under the other ports. The functions defined here can be + // used under all platforms to convert between them without using any + // preprocessor checks. + +#ifdef wxHAS_DPI_INDEPENDENT_PIXELS + static wxSize FromPhys(wxSize sz, const wxWindowBase* w); +#else + static wxSize FromPhys(const wxSize& sz, const wxWindowBase* WXUNUSED(w)) + { + return sz; + } +#endif + static wxPoint FromPhys(const wxPoint& pt, const wxWindowBase* w) + { + const wxSize sz = FromPhys(wxSize(pt.x, pt.y), w); + return wxPoint(sz.x, sz.y); + } + static int FromPhys(int d, const wxWindowBase* w) + { + return FromPhys(wxSize(d, 0), w).x; + } + + wxSize FromPhys(const wxSize& sz) const { return FromPhys(sz, this); } + wxPoint FromPhys(const wxPoint& pt) const { return FromPhys(pt, this); } + int FromPhys(int d) const { return FromPhys(d, this); } + +#ifdef wxHAS_DPI_INDEPENDENT_PIXELS + static wxSize ToPhys(wxSize sz, const wxWindowBase* w); +#else + static wxSize ToPhys(const wxSize& sz, const wxWindowBase* WXUNUSED(w)) + { + return sz; + } +#endif // wxHAS_DPI_INDEPENDENT_PIXELS + static wxPoint ToPhys(const wxPoint& pt, const wxWindowBase* w) + { + const wxSize sz = ToPhys(wxSize(pt.x, pt.y), w); + return wxPoint(sz.x, sz.y); + } + static int ToPhys(int d, const wxWindowBase* w) + { + return ToPhys(wxSize(d, 0), w).x; + } + + wxSize ToPhys(const wxSize& sz) const { return ToPhys(sz, this); } + wxPoint ToPhys(const wxPoint& pt) const { return ToPhys(pt, this); } + int ToPhys(int d) const { return ToPhys(d, this); } + + // DPI-independent pixels, or DIPs, are pixel values for the standard // 96 DPI display, they are scaled to take the current resolution into // account (i.e. multiplied by the same factor as returned by diff --git a/interface/wx/window.h b/interface/wx/window.h index 98f20adce4..4fb7195870 100644 --- a/interface/wx/window.h +++ b/interface/wx/window.h @@ -1198,6 +1198,103 @@ public: /// @overload static int ToDIP(int d, const wxWindow* w); + + /** + Convert from physical pixels to logical pixels. + + All window coordinates in wxWidgets API are always expressed in logical + pixels, but the meaning of logical pixels depends on the platform. + Physical pixels always mean the same thing and refer to the actual + display pixels or, also, sizes of the bitmaps. Under some platforms + logical pixels are actually the same as physical ones (this is the case + for MSW), but under other platforms (e.g. GTK or macOS) this is not the + case and GetContentScaleFactor() defines the ratio between one logical + and one physical pixel. + + This function can be used to convert a value in physical pixels to + logical pixels independently of the platform used. It simply does + nothing under MSW, but divides the input value by the content scale + factor under the other platforms. + + Note that dividing an integer value by scale factor doesn't always + yield an integer value. This function always round the resulting value + up, e.g. 15 physical pixels are translated to 8, not 7, logical pixels + in 200% DPI scaling. This ensures that a physical bitmap of size 15 is + not truncated if the result of this function is used to create a window + to show it, but it does mean that there will be one extra pixel left. + + @see FromDIP(), ToPhys() + + @since 3.1.6 + */ + wxSize FromPhys(const wxSize& sz) const; + + /// @overload + wxPoint FromPhys(const wxPoint& pt) const; + + /// @overload + int FromPhys(int d) const; + + /** + Convert from physical pixels to logical pixels for any window. + + This function can be used without any window pointer, i.e. @a w can be + @NULL. In this case, it uses the content scale factor of the main + screen if supported or just does nothing (i.e. uses scale factor of 1) + otherwise. + + Using member overloads is always preferable, if possible, as they + always use the actually appropriate content scale factor. + + @since 3.1.6 + */ + static wxSize FromPhys(const wxSize& sz, const wxWindow* w); + + /// @overload + static wxPoint FromPhys(const wxPoint& pt, const wxWindow* w); + + /// @overload + static int FromPhys(int d, const wxWindow* w); + + + /** + Convert from logical pixels to physical pixels. + + This function performs the transformation in the converse direction + compared to FromPhys(). + + @since 3.1.6 + */ + wxSize ToPhys(const wxSize& sz) const; + + /// @overload + wxPoint ToPhys(const wxPoint& pt) const; + + /// @overload + int ToPhys(int d) const; + + /** + Convert from logical pixels to physical pixels for any window. + + This function can be used without any window pointer, i.e. @a w can be + @NULL. In this case, it uses the content scale factor of the main + screen if supported or just does nothing (i.e. uses scale factor of 1) + otherwise. + + Using member overloads is always preferable, if possible, as they + always use the actually appropriate content scale factor. + + @since 3.1.6 + */ + static wxSize ToPhys(const wxSize& sz, const wxWindow* w); + + /// @overload + static wxPoint ToPhys(const wxPoint& pt, const wxWindow* w); + + /// @overload + static int ToPhys(int d, const wxWindow* w); + + /** This functions returns the best acceptable minimal size for the window. diff --git a/src/common/statbmpcmn.cpp b/src/common/statbmpcmn.cpp index 95500ba1d1..1741c471e8 100644 --- a/src/common/statbmpcmn.cpp +++ b/src/common/statbmpcmn.cpp @@ -24,8 +24,6 @@ #include "wx/statbmp.h" -#include - extern WXDLLEXPORT_DATA(const char) wxStaticBitmapNameStr[] = "staticBitmap"; // --------------------------------------------------------------------------- @@ -94,20 +92,7 @@ wxStaticBitmapBase::~wxStaticBitmapBase() wxSize wxStaticBitmapBase::DoGetBestSize() const { if ( m_bitmapBundle.IsOk() ) - { - // We return the scaled (i.e. in logical pixels) size of the bitmap - // that would be returned by GetBitmap(), but without bothering to - // actually create the bitmap here. - // - // Note that we can use content scale factor rather than DPI scale - // because the scaled size is the same as normal size on platforms - // without wxHAS_DPI_INDEPENDENT_PIXELS (e.g. wxMSW) anyhow. - const wxSize size = m_bitmapBundle.GetPreferredSizeFor(this); - const double scale = GetContentScaleFactor(); - - // We have to round up the size to avoid truncating the bitmap. - return wxSize(ceil(size.x/scale), ceil(size.y/scale)); - } + return FromPhys(m_bitmapBundle.GetPreferredSizeFor(this)); // the fall back size is completely arbitrary return wxSize(16, 16); diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 09f15fca0b..50eb1be142 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -77,6 +77,13 @@ #include "wx/private/rescale.h" #include "wx/private/window.h" +#if defined(__WXOSX__) + // We need wxOSXGetMainScreenContentScaleFactor() declaration. + #include "wx/osx/core/private.h" +#endif + +#include + // Windows List WXDLLIMPEXP_DATA_CORE(wxWindowList) wxTopLevelWindows; @@ -2859,7 +2866,7 @@ void wxWindowBase::OnInternalIdle() } // ---------------------------------------------------------------------------- -// DPI-independent pixels and dialog units translations +// Conversions between various pixel kinds and dialog units translations // ---------------------------------------------------------------------------- wxSize wxWindowBase::GetDPI() const @@ -2867,7 +2874,70 @@ wxSize wxWindowBase::GetDPI() const return wxDisplay(static_cast(this)).GetPPI(); } -#ifndef wxHAS_DPI_INDEPENDENT_PIXELS +#ifdef wxHAS_DPI_INDEPENDENT_PIXELS + +// In this case logical pixels are DIPs, so we don't need to define conversion +// to/from them (or, rather, they are already defined as trivial inline +// functions in the header), but we do need to define conversions to/from +// physical pixels. + +namespace +{ + +double GetContentScaleFactorFor(const wxWindowBase* w) +{ + if ( w ) + return w->GetContentScaleFactor(); + +#ifdef __WXOSX__ + return wxOSXGetMainScreenContentScaleFactor(); +#else + return 1.0; +#endif +} + +} // anonymous namespace + +/* static */ +wxSize wxWindowBase::FromPhys(wxSize sz, const wxWindowBase* w) +{ + const double scale = GetContentScaleFactorFor(w); + + if ( scale != 1.0 ) + { + // We prefer to round up the size so that conversion from physical + // pixels to logical and back doesn't result in smaller value, as this + // would e.g. truncate the bitmap of odd size when drawing it at 200% + // scaling. Leaving an extra pixel in this case seems like a lesser + // evil, even if not ideal. + if ( sz.x != wxDefaultCoord ) + sz.x = ceil(sz.x) / scale; + if ( sz.y != wxDefaultCoord ) + sz.y = ceil(sz.y) / scale; + } + + return sz; +} + +/* static */ +wxSize wxWindowBase::ToPhys(wxSize sz, const wxWindowBase* w) +{ + const double scale = GetContentScaleFactorFor(w); + + if ( scale != 1.0 ) + { + if ( sz.x != wxDefaultCoord ) + sz.x *= scale; + if ( sz.y != wxDefaultCoord ) + sz.y *= scale; + } + + return sz; +} + +#else // !wxHAS_DPI_INDEPENDENT_PIXELS + +// In this case we have non-trivial implementations for DIP conversions only. namespace { @@ -2910,7 +2980,7 @@ wxWindowBase::ToDIP(const wxSize& sz, const wxWindowBase* w) return wxRescaleCoord(sz).From(dpi).To(baseline); } -#endif // !wxHAS_DPI_INDEPENDENT_PIXELS +#endif // wxHAS_DPI_INDEPENDENT_PIXELS/!wxHAS_DPI_INDEPENDENT_PIXELS // Windows' computes dialog units using average character width over upper- // and lower-case ASCII alphabet and not using the average character width From 0c3b25eccb53d7c6633911f4fd07a2da843f814e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 13 Jan 2022 18:48:54 +0000 Subject: [PATCH 2/2] Use wxWindow::FromPhys() in wxAUI code This is simpler and a tad more accurate than dividing by GetContentScaleFactor(). Also add FromPhys() to MSW-specific code: even if it's not really needed there (as the conversion is always trivial), it's still better to have it for consistency. --- src/aui/tabart.cpp | 6 +++--- src/aui/tabartmsw.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/aui/tabart.cpp b/src/aui/tabart.cpp index 8ceb85d5cb..eaffdd38fe 100644 --- a/src/aui/tabart.cpp +++ b/src/aui/tabart.cpp @@ -248,9 +248,9 @@ void wxAuiGenericTabArt::SetSizingInfo(const wxSize& tab_ctrl_size, int tot_width = (int)tab_ctrl_size.x - GetIndentSize() - wnd->FromDIP(4); if (m_flags & wxAUI_NB_CLOSE_BUTTON) - tot_width -= m_activeCloseBmp.GetPreferredSizeFor(wnd).x / wnd->GetContentScaleFactor(); + tot_width -= wnd->FromPhys(m_activeCloseBmp.GetPreferredSizeFor(wnd).x); if (m_flags & wxAUI_NB_WINDOWLIST_BUTTON) - tot_width -= m_activeWindowListBmp.GetPreferredSizeFor(wnd).x / wnd->GetContentScaleFactor(); + tot_width -= wnd->FromPhys(m_activeWindowListBmp.GetPreferredSizeFor(wnd).x); if (tab_count > 0) { @@ -705,7 +705,7 @@ wxSize wxAuiGenericTabArt::GetTabSize(wxDC& dc, { // we need the correct size of the bitmap to be used on this window in // logical dimensions for drawing - const wxSize bitmapSize = bitmap.GetPreferredSizeFor(wnd) / wnd->GetContentScaleFactor(); + const wxSize bitmapSize = wnd->FromPhys(bitmap.GetPreferredSizeFor(wnd)); // increase by bitmap plus right side bitmap padding tab_width += bitmapSize.x + wnd->FromDIP(3); diff --git a/src/aui/tabartmsw.cpp b/src/aui/tabartmsw.cpp index 52c9091543..2c62ad9230 100644 --- a/src/aui/tabartmsw.cpp +++ b/src/aui/tabartmsw.cpp @@ -306,7 +306,7 @@ wxSize wxAuiMSWTabArt::GetTabSize(wxDC& dc, // if there's a bitmap, add space for it if ( bitmap.IsOk() ) { - const wxSize bitmapSize = bitmap.GetPreferredSizeFor(wnd); + const wxSize bitmapSize = wnd->FromPhys(bitmap.GetPreferredSizeFor(wnd)); tabWidth += bitmapSize.x + wnd->FromDIP(3); // bitmap padding tabHeight = wxMax(tabHeight, bitmapSize.y + wnd->FromDIP(2));