diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/GCWin32Tests.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/GCWin32Tests.java index d21c6ab0e42..1bc3adab734 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/GCWin32Tests.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/GCWin32Tests.java @@ -16,7 +16,6 @@ import static org.junit.Assert.assertEquals; import java.util.concurrent.*; -import java.util.concurrent.atomic.*; import org.eclipse.swt.*; import org.eclipse.swt.internal.*; @@ -30,30 +29,26 @@ class GCWin32Tests { @Test public void gcZoomLevelMustChangeOnShellZoomChange() { - Shell shell = new Shell(Display.getDefault()); + checkGcZoomLevelOnCanvas(DPIUtil.getNativeDeviceZoom()); + checkGcZoomLevelOnCanvas(DPIUtil.getNativeDeviceZoom()*2); + } + private void checkGcZoomLevelOnCanvas(int expectedZoom) { + Display display = Display.getDefault(); + Shell shell = new Shell(display); CompletableFuture gcNativeZoom = new CompletableFuture<>(); - CompletableFuture scaledGcNativeZoom = new CompletableFuture<>(); - int zoom = DPIUtil.getDeviceZoom(); - AtomicBoolean isScaled = new AtomicBoolean(false); - shell.addListener(SWT.Paint, event -> { - if (isScaled.get()) { - scaledGcNativeZoom.complete(event.gc.getGCData().nativeZoom); - } else { - gcNativeZoom.complete(event.gc.getGCData().nativeZoom); - } - }); - - shell.open(); - assertEquals("GCData must have a zoom level equal to the actual zoom level of the widget/shell", DPIUtil.getNativeDeviceZoom(), (int) gcNativeZoom.join()); - int newSWTZoom = zoom * 2; - DPITestUtil.changeDPIZoom(shell, newSWTZoom); - isScaled.set(true); - shell.setVisible(false); - shell.setVisible(true); + Canvas canvas = new Canvas(shell, SWT.NONE); + canvas.setSize(20, 20); + shell.open (); + canvas.addPaintListener(event -> { + gcNativeZoom.complete(event.gc.getGCData().nativeZoom); + }); - assertEquals("GCData must have a zoom level equal to the actual zoom level of the widget/shell on zoomChanged event", newSWTZoom, (int) scaledGcNativeZoom.join()); + DPITestUtil.changeDPIZoom(shell, expectedZoom); + canvas.update(); + assertEquals("GCData must have a zoom level equal to the actual zoom level of the widget/shell", expectedZoom, (int) gcNativeZoom.join()); + shell.dispose(); } @Test 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 eaa1f01f500..1bddf267bc7 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 @@ -60,12 +60,12 @@ public void testCorrectScaleUpUsingDifferentSetBoundsMethod() { DPIUtil.setMonitorSpecificScaling(true); Display display = Display.getDefault(); Shell shell = new Shell(display); - DPITestUtil.changeDPIZoom(shell, 175); - Button button = new Button(shell, SWT.PUSH); button.setText("Widget Test"); - button.setBounds(new Rectangle(0, 47, 200, 47)); shell.open(); + DPITestUtil.changeDPIZoom(shell, 175); + + button.setBounds(new Rectangle(0, 47, 200, 47)); assertEquals("Control::setBounds(Rectangle) doesn't scale up correctly", new Rectangle(0, 82, 350, 83), button.getBoundsInPixels()); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java index e60309bde07..ab059c02332 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java @@ -4936,21 +4936,22 @@ LRESULT WM_DESTROY (long wParam, long lParam) { return null; } +void handleMonitorSpecificDpiChange(int newNativeZoom, Rectangle newBoundsInPixels) { + float scalingFactor = 1f * DPIUtil.getZoomForAutoscaleProperty(newNativeZoom) / DPIUtil.getZoomForAutoscaleProperty(nativeZoom); + DPIUtil.setDeviceZoom (newNativeZoom); + DPIZoomChangeRegistry.applyChange(this, newNativeZoom, scalingFactor); + this.setBoundsInPixels(newBoundsInPixels.x, newBoundsInPixels.y, newBoundsInPixels.width, newBoundsInPixels.height); +} + LRESULT WM_DPICHANGED (long wParam, long lParam) { // Map DPI to Zoom and compare int newNativeZoom = DPIUtil.mapDPIToZoom (OS.HIWORD (wParam)); if (getDisplay().isRescalingAtRuntime()) { Device.win32_destroyUnusedHandles(getDisplay()); - int oldNativeZoom = nativeZoom; - if (newNativeZoom != oldNativeZoom) { - DPIUtil.setDeviceZoom (newNativeZoom); - - float scalingFactor = 1f * DPIUtil.getZoomForAutoscaleProperty(newNativeZoom) / DPIUtil.getZoomForAutoscaleProperty(oldNativeZoom); - DPIZoomChangeRegistry.applyChange(this, newNativeZoom, scalingFactor); - + if (newNativeZoom != nativeZoom) { RECT rect = new RECT (); COM.MoveMemory(rect, lParam, RECT.sizeof); - this.setBoundsInPixels(rect.left, rect.top, rect.right - rect.left, rect.bottom-rect.top); + handleMonitorSpecificDpiChange(newNativeZoom, new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom-rect.top)); return LRESULT.ZERO; } } else { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Shell.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Shell.java index f9e47a1fc90..c3a388b770b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Shell.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Shell.java @@ -2685,6 +2685,26 @@ LRESULT WM_WINDOWPOSCHANGING (long wParam, long lParam) { return result; } +@Override +LRESULT WM_WINDOWPOSCHANGED (long wParam, long lParam) { + LRESULT result = super.WM_WINDOWPOSCHANGED(wParam, lParam); + // When the process is started with System DPI awareness and + // only the thread is PerMonitorV2 aware, there are some scenarios, when the + // OS does not send a DPI change event when a child Shell is positioned and + // opened on another monitor as its parent Shell. To work around that limitation + // this check is added to trigger a dpi change event if an unexpected DPI value is + // detected. + if (display.isRescalingAtRuntime()) { + int dpiForWindow = DPIUtil.mapDPIToZoom(OS.GetDpiForWindow(getShell().handle)); + if (dpiForWindow != nativeZoom) { + WINDOWPOS lpwp = new WINDOWPOS (); + OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof); + handleMonitorSpecificDpiChange(dpiForWindow, new Rectangle(lpwp.x, lpwp.y, lpwp.cx, lpwp.cy)); + } + } + return result; +} + private static void handleDPIChange(Widget widget, int newZoom, float scalingFactor) { if (!(widget instanceof Shell shell)) { return;