diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/ResetMonitorSpecificScalingExtension.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/ResetMonitorSpecificScalingExtension.java index a07812ce74..8a3085aed0 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/ResetMonitorSpecificScalingExtension.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/ResetMonitorSpecificScalingExtension.java @@ -25,13 +25,13 @@ protected ResetMonitorSpecificScalingExtension() { @Override public void beforeEach(ExtensionContext context) throws Exception { - wasMonitorSpecificScalingActive = DPIUtil.isMonitorSpecificScalingActive(); + wasMonitorSpecificScalingActive = Win32DPIUtils.isMonitorSpecificScalingActive(); Display.getDefault().dispose(); } @Override public void afterEach(ExtensionContext context) throws Exception { - DPIUtil.setMonitorSpecificScaling(wasMonitorSpecificScalingActive); + Win32DPIUtils.setMonitorSpecificScaling(wasMonitorSpecificScalingActive); Display.getDefault().dispose(); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/WithMonitorSpecificScalingExtension.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/WithMonitorSpecificScalingExtension.java index d75ee1c90d..5df9c4835f 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/WithMonitorSpecificScalingExtension.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/WithMonitorSpecificScalingExtension.java @@ -24,7 +24,7 @@ private WithMonitorSpecificScalingExtension() { @Override public void beforeEach(ExtensionContext context) throws Exception { super.beforeEach(context); - DPIUtil.setMonitorSpecificScaling(true); + Win32DPIUtils.setMonitorSpecificScaling(true); } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java index 3da919d191..80e85cd9b4 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ControlWin32Tests.java @@ -36,7 +36,7 @@ class ControlWin32Tests { @Test public void testScaleFontCorrectlyInAutoScaleScenario() { - DPIUtil.setMonitorSpecificScaling(true); + Win32DPIUtils.setMonitorSpecificScaling(true); Display display = Display.getDefault(); assertTrue("Autoscale property is not set to true", display.isRescalingAtRuntime()); @@ -48,7 +48,7 @@ public void testScaleFontCorrectlyInAutoScaleScenario() { @Test public void testSetFontWithMonitorSpecificScalingEnabled() { - DPIUtil.setMonitorSpecificScaling(true); + Win32DPIUtils.setMonitorSpecificScaling(true); Display display = Display.getDefault(); Image colorImage = new Image(display, 10, 10); GC gc = new GC(colorImage); @@ -59,7 +59,7 @@ public void testSetFontWithMonitorSpecificScalingEnabled() { @Test public void testScaleFontCorrectlyInNoAutoScaleScenario() { - DPIUtil.setMonitorSpecificScaling(false); + Win32DPIUtils.setMonitorSpecificScaling(false); Display display = Display.getDefault(); assertFalse("Autoscale property is not set to false", display.isRescalingAtRuntime()); @@ -71,7 +71,7 @@ public void testScaleFontCorrectlyInNoAutoScaleScenario() { @Test public void testDoNotScaleFontInNoAutoScaleScenarioWithLegacyFontRegistry() { - DPIUtil.setMonitorSpecificScaling(false); + Win32DPIUtils.setMonitorSpecificScaling(false); String originalValue = System.getProperty("swt.fontRegistry"); System.setProperty("swt.fontRegistry", "legacy"); try { @@ -93,7 +93,7 @@ public void testDoNotScaleFontInNoAutoScaleScenarioWithLegacyFontRegistry() { @Test public void testCorrectScaleUpUsingDifferentSetBoundsMethod() { - DPIUtil.setMonitorSpecificScaling(true); + Win32DPIUtils.setMonitorSpecificScaling(true); Display display = Display.getDefault(); Shell shell = new Shell(display); Button button = new Button(shell, SWT.PUSH); @@ -114,7 +114,7 @@ public void testCorrectScaleUpUsingDifferentSetBoundsMethod() { @CsvSource({ "0.5, 100, true", "1.0, 200, true", "2.0, 200, true", "2.0, quarter, true", "0.5, 100, false", "1.0, 200, false", "2.0, 200, false", "2.0, quarter, false", }) public void testAutoScaleImageData(float scaleFactor, String autoScale, boolean monitorSpecificScaling) { - DPIUtil.setMonitorSpecificScaling(monitorSpecificScaling); + Win32DPIUtils.setMonitorSpecificScaling(monitorSpecificScaling); DPIUtil.runWithAutoScaleValue(autoScale, () -> { Display display = new Display(); try { diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/DisplayWin32Test.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/DisplayWin32Test.java index 043ddd3c74..3338d51049 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/DisplayWin32Test.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/DisplayWin32Test.java @@ -13,7 +13,7 @@ class DisplayWin32Test { @Test public void monitorSpecificScaling_activate() { - DPIUtil.setMonitorSpecificScaling(true); + Win32DPIUtils.setMonitorSpecificScaling(true); Display display = Display.getDefault(); assertTrue(display.isRescalingAtRuntime()); assertEquals(OS.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, OS.GetThreadDpiAwarenessContext()); @@ -21,7 +21,7 @@ public void monitorSpecificScaling_activate() { @Test public void monitorSpecificScaling_deactivate() { - DPIUtil.setMonitorSpecificScaling(false); + Win32DPIUtils.setMonitorSpecificScaling(false); Display display = Display.getDefault(); assertFalse(display.isRescalingAtRuntime()); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java index 6f4258022b..1e101a8e61 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java @@ -1831,22 +1831,5 @@ public static void drawScaled(GC gc, ImageData imageData, int width, int height, imageToDraw.dispose(); } -private final class CocoaDPIUtil { - - /** - * Auto-scale down int dimensions. - */ - public static int pixelToPoint(int size) { - return DPIUtil.pixelToPoint(size, DPIUtil.getDeviceZoom()); - } - - /** - * Auto-scale down float dimensions. - */ - public static float pixelToPoint(float size) { - return DPIUtil.pixelToPoint(size, DPIUtil.getDeviceZoom()); - } -} - } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/internal/CocoaDPIUtil.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/internal/CocoaDPIUtil.java new file mode 100644 index 0000000000..b74e513160 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/internal/CocoaDPIUtil.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2025 Yatta Solutions and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Yatta Solutions - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.internal; + +public class CocoaDPIUtil { + + /** + * Auto-scale down int dimensions. + */ + public static int pixelToPoint(int size) { + return DPIUtil.pixelToPoint(size, DPIUtil.getDeviceZoom()); + } + + /** + * Auto-scale down float dimensions. + */ + public static float pixelToPoint(float size) { + return DPIUtil.pixelToPoint(size, DPIUtil.getDeviceZoom()); + } + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java index 92869144e8..6dce4cff80 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/DPIUtil.java @@ -96,17 +96,6 @@ public static Optional forString(String s) { */ private static final String SWT_AUTOSCALE_METHOD = "swt.autoScale.method"; - /** - * System property to enable to scale the application on runtime - * when a DPI change is detected. - * - * Important: This flag is only parsed and used on Win32. Setting it to - * true on GTK or cocoa will be ignored. - */ - private static final String SWT_AUTOSCALE_UPDATE_ON_RUNTIME = "swt.autoScale.updateOnRuntime"; static { autoScaleValue = System.getProperty (SWT_AUTOSCALE); @@ -115,6 +104,13 @@ public static Optional forString(String s) { autoScaleMethod = AUTO_SCALE_METHOD_SETTING != AutoScaleMethod.AUTO ? AUTO_SCALE_METHOD_SETTING : AutoScaleMethod.NEAREST; } +static String getAutoScaleValue() { + return autoScaleValue; +} + +static void setAutoScaleValue(String autoScaleValueArg) { + autoScaleValue = autoScaleValueArg; +} public static int pixelToPoint(int size, int zoom) { if (zoom == 100 || size == SWT.DEFAULT) return size; @@ -327,7 +323,7 @@ public static void setDeviceZoom (int nativeDeviceZoom) { // in GTK, preserve the current method when switching to a 100% monitor boolean preserveScalingMethod = SWT.getPlatform().equals("gtk") && deviceZoom == 100; if (!preserveScalingMethod && AUTO_SCALE_METHOD_SETTING == AutoScaleMethod.AUTO) { - if (sholdUseSmoothScaling()) { + if (useSmoothScalingByDefaultProvider.shouldUseSmoothScaling()) { autoScaleMethod = AutoScaleMethod.SMOOTH; } else { autoScaleMethod = AutoScaleMethod.NEAREST; @@ -335,12 +331,15 @@ public static void setDeviceZoom (int nativeDeviceZoom) { } } -private static boolean sholdUseSmoothScaling() { - return switch (SWT.getPlatform()) { - case "gtk" -> deviceZoom / 100 * 100 != deviceZoom; - case "win32" -> isMonitorSpecificScalingActive(); - default -> false; - }; +@FunctionalInterface +interface UseSmoothScalingProvider { + boolean shouldUseSmoothScaling(); +} + +private static UseSmoothScalingProvider useSmoothScalingByDefaultProvider = () -> false; + +static void setUseSmoothScalingByDefaultProvider(UseSmoothScalingProvider provider) { + useSmoothScalingByDefaultProvider = provider; } public static int getZoomForAutoscaleProperty (int nativeDeviceZoom) { @@ -388,54 +387,4 @@ public static void runWithAutoScaleValue(String autoScaleValue, Runnable runnabl } } -public static void setMonitorSpecificScaling(boolean activate) { - System.setProperty(SWT_AUTOSCALE_UPDATE_ON_RUNTIME, Boolean.toString(activate)); -} - -public static boolean isMonitorSpecificScalingActive() { - boolean updateOnRuntimeValue = Boolean.getBoolean (SWT_AUTOSCALE_UPDATE_ON_RUNTIME); - return updateOnRuntimeValue; -} - -public static void setAutoScaleForMonitorSpecificScaling() { - boolean isDefaultAutoScale = autoScaleValue == null; - if (isDefaultAutoScale) { - autoScaleValue = "quarter"; - } else if (!isSupportedAutoScaleForMonitorSpecificScaling()) { - throw new SWTError(SWT.ERROR_NOT_IMPLEMENTED, - "monitor-specific scaling is only implemented for auto-scale values \"quarter\", \"exact\", \"false\" or a concrete zoom value, but \"" - + autoScaleValue + "\" has been specified"); - } -} - -/** - * Monitor-specific scaling on Windows only supports auto-scale modes in which - * all elements (font, images, control bounds etc.) are scaled equally or almost - * equally. The previously default mode "integer"/"integer200", which rounded - * the scale factor for everything but fonts to multiples of 100, is complex and - * difficult to realize with monitor-specific rescaling of UI elements. Since a - * uniform scale factor for everything should perspectively be used anyway, - * there will be support for complex auto-scale modes for monitor-specific - * scaling. - * - * The supported modes are "quarter" and "exact" or explicit zoom values given - * by the value itself or "false". Every other value will be treated as - * "integer"/"integer200" and is thus not supported. - */ -private static boolean isSupportedAutoScaleForMonitorSpecificScaling() { - if (autoScaleValue == null) { - return false; - } - switch (autoScaleValue.toLowerCase()) { - case "false", "quarter", "exact": return true; - } - try { - Integer.parseInt(autoScaleValue); - return true; - } catch (NumberFormatException e) { - // unsupported value, use default - } - return false; -} - } \ No newline at end of file diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java index 8234decc0b..bea752b53a 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java @@ -1592,17 +1592,4 @@ public static void drawScaled(GC gc, ImageData imageData, int width, int height, imageToDraw.dispose(); } -private final class GtkDPIUtil { - - /** - * Auto-scale up ImageData to device zoom that is at 100%. - */ - public static ImageData pointToPixel (Device device, final ImageData imageData) { - int imageDataZoomFactor = 100; - if (DPIUtil.getDeviceZoom() == imageDataZoomFactor || imageData == null || (device != null && !device.isAutoScalable())) return imageData; - float scaleFactor = (float) DPIUtil.getDeviceZoom() / imageDataZoomFactor; - return DPIUtil.autoScaleImageData(device, imageData, scaleFactor); - } -} - } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/GtkDPIUtil.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/GtkDPIUtil.java new file mode 100644 index 0000000000..55691884bc --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/GtkDPIUtil.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2025 Yatta Solutions and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Yatta Solutions - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.internal; + +import org.eclipse.swt.graphics.*; + +public class GtkDPIUtil { + static { + DPIUtil.setUseSmoothScalingByDefaultProvider(() -> { + return DPIUtil.getDeviceZoom() / 100 * 100 != DPIUtil.getDeviceZoom(); + }); + } + + /** + * Auto-scale up ImageData to device zoom that is at 100%. + */ + public static ImageData pointToPixel (Device device, final ImageData imageData) { + int imageDataZoomFactor = 100; + if (DPIUtil.getDeviceZoom() == imageDataZoomFactor || imageData == null || (device != null && !device.isAutoScalable())) return imageData; + float scaleFactor = (float) DPIUtil.getDeviceZoom() / imageDataZoomFactor; + return DPIUtil.autoScaleImageData(device, imageData, scaleFactor); + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/Win32DPIUtils.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/Win32DPIUtils.java index 6d21d20be7..86326e7193 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/Win32DPIUtils.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/Win32DPIUtils.java @@ -32,6 +32,22 @@ * @noreference This class is not intended to be referenced by clients */ public class Win32DPIUtils { + /** + * System property to enable to scale the application on runtime + * when a DPI change is detected. + * + * Important: This flag is only parsed and used on Win32. Setting it to + * true on GTK or cocoa will be ignored. + */ + private static final String SWT_AUTOSCALE_UPDATE_ON_RUNTIME = "swt.autoScale.updateOnRuntime"; + + static { + DPIUtil.setUseSmoothScalingByDefaultProvider(() -> isMonitorSpecificScalingActive()); + } + public static boolean setDPIAwareness(int desiredDpiAwareness) { if (desiredDpiAwareness == OS.GetThreadDpiAwarenessContext()) { return true; @@ -236,6 +252,56 @@ public static Rectangle pointToPixel(Drawable drawable, Rectangle rect, int zoom return pointToPixel (rect, zoom); } + public static void setMonitorSpecificScaling(boolean activate) { + System.setProperty(SWT_AUTOSCALE_UPDATE_ON_RUNTIME, Boolean.toString(activate)); + } + + public static void setAutoScaleForMonitorSpecificScaling() { + boolean isDefaultAutoScale = DPIUtil.getAutoScaleValue() == null; + if (isDefaultAutoScale) { + DPIUtil.setAutoScaleValue("quarter"); + } else if (!isSupportedAutoScaleForMonitorSpecificScaling()) { + throw new SWTError(SWT.ERROR_NOT_IMPLEMENTED, + "monitor-specific scaling is only implemented for auto-scale values \"quarter\", \"exact\", \"false\" or a concrete zoom value, but \"" + + DPIUtil.getAutoScaleValue() + "\" has been specified"); + } + } + + /** + * Monitor-specific scaling on Windows only supports auto-scale modes in which + * all elements (font, images, control bounds etc.) are scaled equally or almost + * equally. The previously default mode "integer"/"integer200", which rounded + * the scale factor for everything but fonts to multiples of 100, is complex and + * difficult to realize with monitor-specific rescaling of UI elements. Since a + * uniform scale factor for everything should perspectively be used anyway, + * there will be support for complex auto-scale modes for monitor-specific + * scaling. + * + * The supported modes are "quarter" and "exact" or explicit zoom values given + * by the value itself or "false". Every other value will be treated as + * "integer"/"integer200" and is thus not supported. + */ + private static boolean isSupportedAutoScaleForMonitorSpecificScaling() { + if (DPIUtil.getAutoScaleValue() == null) { + return false; + } + switch (DPIUtil.getAutoScaleValue().toLowerCase()) { + case "false", "quarter", "exact": return true; + } + try { + Integer.parseInt(DPIUtil.getAutoScaleValue()); + return true; + } catch (NumberFormatException e) { + // unsupported value, use default + } + return false; + } + + public static boolean isMonitorSpecificScalingActive() { + boolean updateOnRuntimeValue = Boolean.getBoolean (SWT_AUTOSCALE_UPDATE_ON_RUNTIME); + return updateOnRuntimeValue; + } + /** * AutoScale ImageDataProvider. */ diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java index 86af0e48be..1ce5dbc215 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java @@ -954,9 +954,9 @@ public void close () { protected void create (DeviceData data) { checkSubclass (); checkDisplay (thread = Thread.currentThread (), true); - if (DPIUtil.isMonitorSpecificScalingActive()) { + if (Win32DPIUtils.isMonitorSpecificScalingActive()) { setMonitorSpecificScaling(true); - DPIUtil.setAutoScaleForMonitorSpecificScaling(); + Win32DPIUtils.setAutoScaleForMonitorSpecificScaling(); } createDisplay (data); register (this);