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/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)); 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