16
16
17
17
18
18
import java .util .*;
19
+ import java .util .concurrent .atomic .*;
19
20
import java .util .stream .*;
20
21
21
22
import org .eclipse .swt .*;
@@ -78,6 +79,9 @@ public abstract class Control extends Widget implements Drawable {
78
79
Font font ;
79
80
int drawCount , foreground , background , backgroundAlpha = 255 ;
80
81
82
+ /** Cache for currently processed DPI change event to be able to cancel it if a new one is triggered */
83
+ Event currentDpiChangeEvent ;
84
+
81
85
/**
82
86
* Prevents uninitialized instances from being created outside the package.
83
87
*/
@@ -4760,7 +4764,10 @@ public boolean setParent (Composite parent) {
4760
4764
if (parent .nativeZoom != nativeZoom ) {
4761
4765
int newZoom = parent .nativeZoom ;
4762
4766
Event zoomChangedEvent = createZoomChangedEvent (newZoom );
4763
- notifyListeners (SWT .ZoomChanged , zoomChangedEvent );
4767
+ if (currentDpiChangeEvent != null ) {
4768
+ currentDpiChangeEvent .doit = false ;
4769
+ }
4770
+ sendZoomChangedEvent (zoomChangedEvent , getShell ());
4764
4771
}
4765
4772
int flags = OS .SWP_NOSIZE | OS .SWP_NOMOVE | OS .SWP_NOACTIVATE ;
4766
4773
OS .SetWindowPos (topHandle , OS .HWND_BOTTOM , 0 , 0 , 0 , 0 , flags );
@@ -4953,19 +4960,24 @@ LRESULT WM_DESTROY (long wParam, long lParam) {
4953
4960
return null ;
4954
4961
}
4955
4962
4956
- void handleMonitorSpecificDpiChange (int newNativeZoom , Rectangle newBoundsInPixels ) {
4963
+ private void handleMonitorSpecificDpiChange (int newNativeZoom , Rectangle newBoundsInPixels ) {
4957
4964
DPIUtil .setDeviceZoom (newNativeZoom );
4958
4965
Event zoomChangedEvent = createZoomChangedEvent (newNativeZoom );
4966
+ if (currentDpiChangeEvent != null ) {
4967
+ currentDpiChangeEvent .doit = false ;
4968
+ }
4969
+ currentDpiChangeEvent = zoomChangedEvent ;
4959
4970
notifyListeners (SWT .ZoomChanged , zoomChangedEvent );
4960
4971
this .setBoundsInPixels (newBoundsInPixels .x , newBoundsInPixels .y , newBoundsInPixels .width , newBoundsInPixels .height );
4961
4972
}
4962
4973
4963
- private Event createZoomChangedEvent (int zoom ) {
4974
+ Event createZoomChangedEvent (int zoom ) {
4964
4975
Event event = new Event ();
4965
4976
event .type = SWT .ZoomChanged ;
4966
4977
event .widget = this ;
4967
4978
event .detail = zoom ;
4968
4979
event .doit = true ;
4980
+ event .data = new DPIChangeExecution ();
4969
4981
return event ;
4970
4982
}
4971
4983
@@ -4974,12 +4986,10 @@ LRESULT WM_DPICHANGED (long wParam, long lParam) {
4974
4986
int newNativeZoom = DPIUtil .mapDPIToZoom (OS .HIWORD (wParam ));
4975
4987
if (getDisplay ().isRescalingAtRuntime ()) {
4976
4988
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 ;
4983
4993
} else {
4984
4994
int newZoom = DPIUtil .getZoomForAutoscaleProperty (newNativeZoom );
4985
4995
int oldZoom = DPIUtil .getZoomForAutoscaleProperty (nativeZoom );
@@ -5879,6 +5889,62 @@ LRESULT wmScrollChild (long wParam, long lParam) {
5879
5889
return null ;
5880
5890
}
5881
5891
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
+ }
5882
5948
5883
5949
@ Override
5884
5950
void handleDPIChange (Event event , float scalingFactor ) {
0 commit comments