From b4c28db765325ea6e84ad7634278d6ec0852353c Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 18 Sep 2025 16:21:03 +0100 Subject: [PATCH] ComponentPeer: Add method for overriding native scale factor --- .../native/juce_Windowing_linux.cpp | 36 +++++++--- .../native/juce_Windowing_windows.cpp | 65 +++++++++++++++---- .../windows/juce_ComponentPeer.h | 19 ++++++ 3 files changed, 98 insertions(+), 22 deletions(-) diff --git a/modules/juce_gui_basics/native/juce_Windowing_linux.cpp b/modules/juce_gui_basics/native/juce_Windowing_linux.cpp index aafb0993f7..9774bac484 100644 --- a/modules/juce_gui_basics/native/juce_Windowing_linux.cpp +++ b/modules/juce_gui_basics/native/juce_Windowing_linux.cpp @@ -106,7 +106,7 @@ public: updateScaleFactorFromNewBounds (bounds, false); auto physicalBounds = parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (bounds) - : bounds * currentScaleFactor; + : bounds * getPlatformScaleFactor(); WeakReference deletionChecker (&component); @@ -140,14 +140,14 @@ public: { auto physicalParentPosition = XWindowSystem::getInstance()->getPhysicalParentScreenPosition(); auto parentPosition = parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalParentPosition) - : physicalParentPosition / currentScaleFactor; + : physicalParentPosition / getPlatformScaleFactor(); auto screenBounds = parentWindow == 0 ? bounds : bounds.translated (parentPosition.x, parentPosition.y); if (physical) return parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (screenBounds.getTopLeft()) - : screenBounds.getTopLeft() * currentScaleFactor; + : screenBounds.getTopLeft() * getPlatformScaleFactor(); return screenBounds.getTopLeft(); } @@ -270,7 +270,7 @@ public: if (trueIfInAChildWindow) return true; - return XWindowSystem::getInstance()->contains (windowH, localPos * currentScaleFactor); + return XWindowSystem::getInstance()->contains (windowH, localPos * getPlatformScaleFactor()); } void toFront (bool makeActive) override @@ -332,7 +332,24 @@ public: double getPlatformScaleFactor() const noexcept override { - return currentScaleFactor; + return scaleFactorOverride.value_or (currentScaleFactor); + } + + void setCustomPlatformScaleFactor (std::optional scaleIn) override + { + const auto prev = getPlatformScaleFactor(); + scaleFactorOverride = scaleIn; + const auto next = getPlatformScaleFactor(); + + if (approximatelyEqual (prev, next)) + return; + + scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (next); }); + } + + std::optional getCustomPlatformScaleFactor() const override + { + return scaleFactorOverride; } void setAlpha (float) override {} @@ -386,7 +403,7 @@ public: updateScaleFactorFromNewBounds (physicalBounds, true); bounds = parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds) - : physicalBounds / currentScaleFactor; + : physicalBounds / getPlatformScaleFactor(); updateVBlankTimer(); } @@ -403,7 +420,7 @@ public: windowBorder = [&]() { if (auto unscaledBorderSize = XWindowSystem::getInstance()->getBorderSize (windowH)) - return OptionalBorderSize { (*unscaledBorderSize).multipliedBy (1.0 / currentScaleFactor) }; + return OptionalBorderSize { (*unscaledBorderSize).multipliedBy (1.0 / getPlatformScaleFactor()) }; return OptionalBorderSize {}; }(); @@ -460,7 +477,7 @@ private: void repaint (Rectangle area) { - regionsNeedingRepaint.add (area * peer.currentScaleFactor); + regionsNeedingRepaint.add (area * peer.getPlatformScaleFactor()); } void performAnyPendingRepaintsNow() @@ -511,7 +528,7 @@ private: auto context = peer.getComponent().getLookAndFeel() .createGraphicsContext (image, -totalArea.getPosition(), adjustedList); - context->addTransform (AffineTransform::scale ((float) peer.currentScaleFactor)); + context->addTransform (AffineTransform::scale ((float) peer.getPlatformScaleFactor())); peer.handlePaint (*context); } @@ -607,6 +624,7 @@ private: Rectangle bounds; ComponentPeer::OptionalBorderSize windowBorder; bool fullScreen = false, isAlwaysOnTop = false; + std::optional scaleFactorOverride; double currentScaleFactor = 1.0; Array glRepaintListeners; ScopedWindowAssociation association; diff --git a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp index ca427f2981..697e8a734f 100644 --- a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp +++ b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp @@ -558,7 +558,8 @@ static Rectangle convertLogicalScreenRectangleToPhysical (Rectangle convertPhysicalScreenPointToLogical (Point p, HWND h) noexcept +template +static Point convertPhysicalScreenPointToLogical (Point p, HWND h) noexcept { if (isPerMonitorDPIAwareWindow (h)) return Desktop::getInstance().getDisplays().physicalToLogical (p, getCurrentDisplayFromScaleFactor (h)); @@ -566,7 +567,8 @@ static Point convertPhysicalScreenPointToLogical (Point p, HWND h) noe return p; } -static Point convertLogicalScreenPointToPhysical (Point p, HWND h) noexcept +template +static Point convertLogicalScreenPointToPhysical (Point p, HWND h) noexcept { if (isPerMonitorDPIAwareWindow (h)) return Desktop::getInstance().getDisplays().logicalToPhysical (p, getCurrentDisplayFromScaleFactor (h)); @@ -1471,8 +1473,19 @@ public: return convertPhysicalScreenPointToLogical (getClientRectInScreen().getPosition(), hwnd); } - Point localToGlobal (Point relativePosition) override { return relativePosition + getScreenPosition().toFloat(); } - Point globalToLocal (Point screenPosition) override { return screenPosition - getScreenPosition().toFloat(); } + Point localToGlobal (Point relativePosition) override + { + const auto localPhysical = relativePosition * getPlatformScaleFactor(); + const auto physical = localPhysical + getClientRectInScreen().getPosition().toFloat(); + return convertPhysicalScreenPointToLogical (physical, hwnd); + } + + Point globalToLocal (Point screenPosition) override + { + const auto physical = convertLogicalScreenPointToPhysical (screenPosition, hwnd); + const auto localPhysical = physical - getClientRectInScreen().getPosition().toFloat(); + return localPhysical / getPlatformScaleFactor(); + } using ComponentPeer::localToGlobal; using ComponentPeer::globalToLocal; @@ -1578,12 +1591,13 @@ public: bool contains (Point localPos, bool trueIfInAChildWindow) const override { - auto r = convertPhysicalScreenRectangleToLogical (D2DUtilities::toRectangle (getWindowScreenRect (hwnd)), hwnd); + const auto localPhysical = localPos.toFloat() / getPlatformScaleFactor(); + auto r = D2DUtilities::toRectangle (getWindowScreenRect (hwnd)).toFloat(); - if (! r.withZeroOrigin().contains (localPos)) + if (! r.withZeroOrigin().contains (localPhysical)) return false; - const auto screenPos = convertLogicalScreenPointToPhysical (localPos + getScreenPosition(), hwnd); + const auto screenPos = (localPhysical + getClientRectInScreen().getPosition().toFloat()).roundToInt(); auto w = WindowFromPoint (D2DUtilities::toPOINT (screenPos)); return w == hwnd || (trueIfInAChildWindow && (IsChild (hwnd, w) != 0)); @@ -1596,7 +1610,7 @@ public: BorderSize getFrameSize() const override { - return findPhysicalBorderSize().value_or (BorderSize{}).multipliedBy (1.0 / scaleFactor); + return findPhysicalBorderSize().value_or (BorderSize{}).multipliedBy (1.0 / getPlatformScaleFactor()); } bool setAlwaysOnTop (bool alwaysOnTop) override @@ -1993,7 +2007,7 @@ public: return peer->doKeyUp (msg.wParam); } - double getPlatformScaleFactor() const noexcept override + double getPlatformScaleFactorWithoutOverride() const noexcept { #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE return 1.0; @@ -2013,6 +2027,31 @@ public: #endif } + double getPlatformScaleFactor() const noexcept override + { + if (scaleFactorOverride.has_value()) + return *scaleFactorOverride; + + return getPlatformScaleFactorWithoutOverride(); + } + + void setCustomPlatformScaleFactor (std::optional scaleIn) override + { + const auto prev = getPlatformScaleFactor(); + scaleFactorOverride = scaleIn; + const auto next = getPlatformScaleFactor(); + + if (approximatelyEqual (prev, next)) + return; + + scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (next); }); + } + + std::optional getCustomPlatformScaleFactor() const override + { + return scaleFactorOverride; + } + static void getLastError() { TCHAR messageBuffer[256] = {}; @@ -2065,7 +2104,6 @@ public: } bool hasTitleBar() const { return (styleFlags & windowHasTitleBar) != 0; } - double getScaleFactor() const { return scaleFactor; } private: HWND hwnd, parentToAddTo; @@ -2083,6 +2121,7 @@ private: #endif double scaleFactor = 1.0; + std::optional scaleFactorOverride; bool inDpiChange = 0, inHandlePositionChanged = 0; HMONITOR currentMonitor = nullptr; @@ -4264,8 +4303,8 @@ private: { if (auto parentHwnd = GetParent (hwnd)) { - auto parentRect = convertPhysicalScreenRectangleToLogical (D2DUtilities::toRectangle (getWindowScreenRect (parentHwnd)), hwnd); - newBounds.translate (parentRect.getX(), parentRect.getY()); + const auto parentRect = convertPhysicalScreenRectangleToLogical (D2DUtilities::toRectangle (getWindowScreenRect (parentHwnd)), hwnd); + newBounds += parentRect.getPosition(); } } @@ -5787,7 +5826,7 @@ static const Displays::Display* getCurrentDisplayFromScaleFactor (HWND hwnd) const auto scaleToLookFor = [&] { if (auto* peer = HWNDComponentPeer::getOwnerOfWindow (hwnd)) - return peer->getPlatformScaleFactor(); + return peer->getPlatformScaleFactorWithoutOverride(); return getScaleFactorForWindow (hwnd); }(); diff --git a/modules/juce_gui_basics/windows/juce_ComponentPeer.h b/modules/juce_gui_basics/windows/juce_ComponentPeer.h index d9f0b99fd5..6eca2d8f2b 100644 --- a/modules/juce_gui_basics/windows/juce_ComponentPeer.h +++ b/modules/juce_gui_basics/windows/juce_ComponentPeer.h @@ -544,6 +544,25 @@ public: */ virtual double getPlatformScaleFactor() const noexcept { return 1.0; } + /** On Windows and Linux, calling this with a non-null optional will override whatever scale + factor the platform has specified for this window. The new scale factor will persist even + in the case that the platform attempts to set a new scale! Pass a null optional to revert + back to the platform-provided scale. + + This is intended for use by plugin wrappers, where hosts may attempt to set a scale factor + different from the platform scale. You should never need to call this directly. + */ + virtual void setCustomPlatformScaleFactor (std::optional) {} + + /** Returns the custom scale factor set using setCustomPlatformScaleFactor(), if any. + + If a custom scale factor has been set, getPlatformScaleFactor() will always return that + value, effectively overriding any scale factor requested by the system. + Otherwise, if the custom platform scale factor is nullopt, then the system will update the + scale factor automatically. + */ + virtual std::optional getCustomPlatformScaleFactor() const { return {}; } + /** On platforms that support it, this will update the window's titlebar in some way to indicate that the window's document needs saving. */