From fcfd25f479f8f4059d314d589c143fb1b8a0fef7 Mon Sep 17 00:00:00 2001 From: Heiko Klare Date: Thu, 16 Oct 2025 15:27:56 +0200 Subject: [PATCH] [Win32] Fix wrong layout when opening shell or reparenting control #2608 In multiple situation, relayouting due to DPI change events does not work as expected: - When opening child shells, they may be relayouted after being opened if their position changed to a different monitor between the OS handle was created and a new position was set - When reparenting controls between different shells, the target shell may not be properly relayout at all Since the user experience when performing these two cases asynchronously is not good anyway (e.g., reparenting leads to unexpected asynchronous relayouting operations), this change adapts the two use cases to process the DPI change synchronously. Fixes https://github.com/eclipse-platform/eclipse.platform.swt/issues/2608 --- .../org/eclipse/swt/widgets/DPITestUtil.java | 2 +- .../org/eclipse/swt/widgets/Control.java | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/DPITestUtil.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/DPITestUtil.java index e85a31c27ea..f105f6bec12 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/DPITestUtil.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/DPITestUtil.java @@ -28,7 +28,7 @@ private DPITestUtil() { public static void changeDPIZoom (Shell shell, int nativeZoom) { DPIUtil.setDeviceZoom(nativeZoom); - Event event = shell.createZoomChangedEvent(nativeZoom); + Event event = shell.createZoomChangedEvent(nativeZoom, true); shell.sendZoomChangedEvent(event, shell); DPIChangeExecution data = (DPIChangeExecution) event.data; waitForDPIChange(shell, TIMEOUT_MILLIS, data.taskCount); 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 878080d76a2..d93c032fd8a 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 @@ -4784,12 +4784,9 @@ public boolean setParent (Composite parent) { if (OS.SetParent (topHandle, parent.handle) == 0) return false; this.parent = parent; // If parent changed, zoom level might need to be adjusted - if (parent.nativeZoom != nativeZoom) { - int newZoom = parent.nativeZoom; - Event zoomChangedEvent = createZoomChangedEvent(newZoom); - if (currentDpiChangeEvent != null) { - currentDpiChangeEvent.doit = false; - } + int newZoom = parent.nativeZoom; + if (newZoom != nativeZoom) { + Event zoomChangedEvent = createZoomChangedEvent(newZoom, false); sendZoomChangedEvent(zoomChangedEvent, getShell()); } int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE; @@ -4985,7 +4982,10 @@ LRESULT WM_DESTROY (long wParam, long lParam) { private void handleMonitorSpecificDpiChange(int newNativeZoom, Rectangle newBoundsInPixels) { DPIUtil.setDeviceZoom (newNativeZoom); - Event zoomChangedEvent = createZoomChangedEvent(newNativeZoom); + // Do not process DPI change for child shells asynchronous to avoid relayouting when + // repositioning the child shell to a different monitor upon opening + boolean processDpiChangeAsynchronous = getShell().getParent() == null; + Event zoomChangedEvent = createZoomChangedEvent(newNativeZoom, processDpiChangeAsynchronous); if (currentDpiChangeEvent != null) { currentDpiChangeEvent.doit = false; } @@ -4994,13 +4994,15 @@ private void handleMonitorSpecificDpiChange(int newNativeZoom, Rectangle newBoun this.setBoundsInPixels(newBoundsInPixels.x, newBoundsInPixels.y, newBoundsInPixels.width, newBoundsInPixels.height); } -Event createZoomChangedEvent(int zoom) { +Event createZoomChangedEvent(int zoom, boolean asyncExec) { Event event = new Event(); event.type = SWT.ZoomChanged; event.widget = this; event.detail = zoom; event.doit = true; - event.data = new DPIChangeExecution(); + DPIChangeExecution dpiChangeExecution = new DPIChangeExecution(); + dpiChangeExecution.asyncExec = asyncExec; + event.data = dpiChangeExecution; return event; }