diff --git a/src/dmt/gui/widget/Shadow.h b/src/dmt/gui/widget/Shadow.h index 0b0c8f9e..af7752de 100644 --- a/src/dmt/gui/widget/Shadow.h +++ b/src/dmt/gui/widget/Shadow.h @@ -249,7 +249,7 @@ class Shadow TRACER("Shadow::drawOuterForPath"); juce::Graphics::ScopedSaveState saveState(_g); juce::Path shadowPath(_target); - shadowPath.addRectangle(_target.getBounds().expanded(10 * scale)); + shadowPath.addRectangle(_target.getBounds().expanded(10.0f)); shadowPath.setUsingNonZeroWinding(false); _g.reduceClipRegion(shadowPath); updateShadowParameters(); @@ -259,19 +259,22 @@ class Shadow private: inline void updateShadowParameters() { - const auto scaledRadius = static_cast(radius * size * scale); - const auto scaledOffset = juce::Point( - static_cast(offset.x) * static_cast(scale), - static_cast(offset.y) * static_cast(scale)); + // Rendering uses a graphics transform for HiDPI, so keep shadow parameters + // in logical units to avoid applying scale twice. + const auto scaledRadius = static_cast(radius * size); + const auto scaledOffset = juce::Point(static_cast(offset.x), + static_cast(offset.y)); if (inner) { - innerShadowRenderer.setColor(*colour).setRadius(scaledRadius).setOffset( - scaledOffset); + innerShadowRenderer.setColor(*colour) + .setRadius(scaledRadius) + .setOffset(scaledOffset); return; } - outerShadowRenderer.setColor(*colour).setRadius(scaledRadius).setOffset( - scaledOffset); + outerShadowRenderer.setColor(*colour) + .setRadius(scaledRadius) + .setOffset(scaledOffset); } inline void refreshCachedImageIfNeeded(bool forceRepaint = false) diff --git a/src/dmt/gui/window/Compositor.h b/src/dmt/gui/window/Compositor.h index a5fbf245..f75f7514 100644 --- a/src/dmt/gui/window/Compositor.h +++ b/src/dmt/gui/window/Compositor.h @@ -570,16 +570,21 @@ class Compositor * components that implement the IScaleable interface receive the updated * size factor. * + * @param force If true, propagation runs even when the size factor itself + * did not change. This is required for dynamically added + * scaleable children. + * * @note This is typically triggered by the Compositor in response to * hierarchy changes or user scaling actions. */ - void propagateSizeFactor() noexcept + void propagateSizeFactor(const bool force = false) noexcept { TRACER("Compositor::propagateSizeFactor"); if (isPropagatingSizeFactor) return; - if (juce::approximatelyEqual(lastPropagatedSizeFactor, sizeFactor)) + if (!force && + juce::approximatelyEqual(lastPropagatedSizeFactor, sizeFactor)) return; const juce::ScopedValueSetter isPropagatingGuard( @@ -755,7 +760,7 @@ class Compositor return; addListenerToChildren(&component); - propagateSizeFactor(); + propagateSizeFactor(true); } private: