Skip to content

Commit 323ee58

Browse files
amartya4256akoch-yatta
authored andcommitted
Make DPI_CHANGED event handling async
With this commit, all the widgets scale themselves asynchronously independent of the order saving the wait time over their children to scale.
1 parent 87fa834 commit 323ee58

File tree

4 files changed

+99
-9
lines changed

4 files changed

+99
-9
lines changed

bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/DPITestUtil.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
*******************************************************************************/
1414
package org.eclipse.swt.internal;
1515

16+
import java.time.*;
17+
import java.util.concurrent.atomic.*;
18+
1619
import org.eclipse.swt.*;
1720
import org.eclipse.swt.widgets.*;
1821

@@ -21,14 +24,34 @@ public final class DPITestUtil {
2124
private DPITestUtil() {
2225
}
2326

27+
private static final int TIMEOUT_MILLIS = 10000;
28+
2429
public static void changeDPIZoom (Shell shell, int nativeZoom) {
2530
DPIUtil.setDeviceZoom(nativeZoom);
2631
Event event = new Event();
2732
event.type = SWT.ZoomChanged;
2833
event.widget = shell;
2934
event.detail = nativeZoom;
3035
event.doit = true;
36+
AtomicInteger scalingCounter = new AtomicInteger(0);
37+
event.data = scalingCounter;
3138
shell.notifyListeners(SWT.ZoomChanged, event);
39+
waitForDPIChange(shell, TIMEOUT_MILLIS, scalingCounter);
40+
}
41+
42+
public static void waitForPassCondition(Shell shell, int timeout, AtomicInteger scalingCounter) {
43+
final Instant timeOut = Instant.now().plusMillis(timeout);
44+
final Display display = shell == null ? Display.getDefault() : shell.getDisplay();
45+
46+
while (Instant.now().isBefore(timeOut) && scalingCounter.get() != 0) {
47+
if (!display.isDisposed()) {
48+
display.readAndDispatch();
49+
}
50+
}
51+
}
52+
53+
public static void waitForDPIChange(Shell shell, int timeout, AtomicInteger scalingCounter) {
54+
waitForPassCondition(shell, timeout, scalingCounter);
3255
}
3356

3457
}

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Composite.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1977,7 +1977,7 @@ public String toString() {
19771977
void handleDPIChange(Event event, float scalingFactor) {
19781978
super.handleDPIChange(event, scalingFactor);
19791979
for (Control child : getChildren()) {
1980-
child.notifyListeners(SWT.ZoomChanged, event);
1980+
child.sendZoomChangedEvent(event, getShell());
19811981
}
19821982
}
19831983
}

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717

1818
import java.util.*;
19+
import java.util.concurrent.atomic.*;
1920
import java.util.stream.*;
2021

2122
import org.eclipse.swt.*;
@@ -78,6 +79,9 @@ public abstract class Control extends Widget implements Drawable {
7879
Font font;
7980
int drawCount, foreground, background, backgroundAlpha = 255;
8081

82+
/** Cache for currently processed DPI change event to be able to cancel it if a new one is triggered */
83+
Event currentDpiChangeEvent;
84+
8185
/**
8286
* Prevents uninitialized instances from being created outside the package.
8387
*/
@@ -4760,7 +4764,10 @@ public boolean setParent (Composite parent) {
47604764
if (parent.nativeZoom != nativeZoom) {
47614765
int newZoom = parent.nativeZoom;
47624766
Event zoomChangedEvent = createZoomChangedEvent(newZoom);
4763-
notifyListeners(SWT.ZoomChanged, zoomChangedEvent);
4767+
if (currentDpiChangeEvent != null) {
4768+
currentDpiChangeEvent.doit = false;
4769+
}
4770+
sendZoomChangedEvent(zoomChangedEvent, getShell());
47644771
}
47654772
int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
47664773
OS.SetWindowPos (topHandle, OS.HWND_BOTTOM, 0, 0, 0, 0, flags);
@@ -4953,9 +4960,13 @@ LRESULT WM_DESTROY (long wParam, long lParam) {
49534960
return null;
49544961
}
49554962

4956-
void handleMonitorSpecificDpiChange(int newNativeZoom, Rectangle newBoundsInPixels) {
4963+
private void handleMonitorSpecificDpiChange(int newNativeZoom, Rectangle newBoundsInPixels) {
49574964
DPIUtil.setDeviceZoom (newNativeZoom);
49584965
Event zoomChangedEvent = createZoomChangedEvent(newNativeZoom);
4966+
if (currentDpiChangeEvent != null) {
4967+
currentDpiChangeEvent.doit = false;
4968+
}
4969+
currentDpiChangeEvent = zoomChangedEvent;
49594970
notifyListeners(SWT.ZoomChanged, zoomChangedEvent);
49604971
this.setBoundsInPixels(newBoundsInPixels.x, newBoundsInPixels.y, newBoundsInPixels.width, newBoundsInPixels.height);
49614972
}
@@ -4966,6 +4977,7 @@ private Event createZoomChangedEvent(int zoom) {
49664977
event.widget = this;
49674978
event.detail = zoom;
49684979
event.doit = true;
4980+
event.data = new DPIChangeExecution();
49694981
return event;
49704982
}
49714983

@@ -4974,12 +4986,10 @@ LRESULT WM_DPICHANGED (long wParam, long lParam) {
49744986
int newNativeZoom = DPIUtil.mapDPIToZoom (OS.HIWORD (wParam));
49754987
if (getDisplay().isRescalingAtRuntime()) {
49764988
Device.win32_destroyUnusedHandles(getDisplay());
4977-
if (newNativeZoom != nativeZoom) {
4978-
RECT rect = new RECT ();
4979-
COM.MoveMemory(rect, lParam, RECT.sizeof);
4980-
handleMonitorSpecificDpiChange(newNativeZoom, new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom-rect.top));
4981-
return LRESULT.ZERO;
4982-
}
4989+
RECT rect = new RECT ();
4990+
COM.MoveMemory(rect, lParam, RECT.sizeof);
4991+
handleMonitorSpecificDpiChange(newNativeZoom, new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom-rect.top));
4992+
return LRESULT.ZERO;
49834993
} else {
49844994
int newZoom = DPIUtil.getZoomForAutoscaleProperty (newNativeZoom);
49854995
int oldZoom = DPIUtil.getZoomForAutoscaleProperty (nativeZoom);
@@ -5879,6 +5889,62 @@ LRESULT wmScrollChild (long wParam, long lParam) {
58795889
return null;
58805890
}
58815891

5892+
private static class DPIChangeExecution {
5893+
private AtomicInteger taskCount = new AtomicInteger();
5894+
private boolean asyncExec = true;
5895+
5896+
private void process(Control control, Runnable operation) {
5897+
boolean currentAsyncExec = asyncExec;
5898+
if (control instanceof Composite comp) {
5899+
// do not execute the DPI change asynchronously, if there is no
5900+
// layout manager available otherwise size calculations could lead
5901+
// to wrong results, because no final layout will be triggered
5902+
asyncExec &= (comp.layout != null);
5903+
}
5904+
if (asyncExec) {
5905+
control.getDisplay().asyncExec(operation::run);
5906+
} else {
5907+
operation.run();
5908+
}
5909+
// resetting it prevents to break asynchronous execution when the synchronous
5910+
// DPI change handling is finished
5911+
asyncExec = currentAsyncExec;
5912+
}
5913+
5914+
private void increment() {
5915+
taskCount.incrementAndGet();
5916+
}
5917+
5918+
private boolean decrement() {
5919+
return taskCount.decrementAndGet() <= 0;
5920+
}
5921+
}
5922+
5923+
void sendZoomChangedEvent(Event event, Shell shell) {
5924+
this.currentDpiChangeEvent = event;
5925+
if (event.data instanceof DPIChangeExecution dpiExecData) {
5926+
dpiExecData.increment();
5927+
dpiExecData.process(this, () -> {
5928+
try {
5929+
if (!this.isDisposed() && event.doit) {
5930+
notifyListeners(SWT.ZoomChanged, event);
5931+
}
5932+
} finally {
5933+
if (shell.isDisposed()) {
5934+
return;
5935+
}
5936+
if (dpiExecData.decrement()) {
5937+
if (event == currentDpiChangeEvent) {
5938+
currentDpiChangeEvent = null;
5939+
}
5940+
if (event.doit) {
5941+
shell.WM_SIZE(0, 0);
5942+
}
5943+
}
5944+
}
5945+
});
5946+
}
5947+
}
58825948

58835949
@Override
58845950
void handleDPIChange(Event event, float scalingFactor) {

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Shell.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ public class Shell extends Decorations {
132132
long toolIcon, balloonIcon;
133133
long windowProc;
134134
Control lastActive;
135+
135136
static /*final*/ long ToolTipProc;
136137
static final long DialogProc;
137138
static final TCHAR DialogClass = new TCHAR (0, "#32770", true);

0 commit comments

Comments
 (0)