1616
1717
1818import java .util .*;
19+ import java .util .concurrent .atomic .*;
1920import java .util .stream .*;
2021
2122import 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,19 +4960,24 @@ 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}
49624973
4963- private Event createZoomChangedEvent (int zoom ) {
4974+ Event createZoomChangedEvent (int zoom ) {
49644975 Event event = new Event ();
49654976 event .type = SWT .ZoomChanged ;
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+ static class DPIChangeExecution {
5893+ 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
58845950void handleDPIChange (Event event , float scalingFactor ) {
0 commit comments