diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/SashForm.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/SashForm.java index 23a9874a20d..29098bd39a6 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/SashForm.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/SashForm.java @@ -287,9 +287,12 @@ void onDragSash(Event event) { ((SashFormData)data2).weight = (((long)b2.height << 16) + area.height - 1) / area.height; } if (correction || (event.doit && event.detail != SWT.DRAG)) { + boolean isWin32 = "win32".equals(SWT.getPlatform()); + if (isWin32) setLayoutDeferred(true); c1.setBounds(b1); sash.setBounds(event.x, event.y, event.width, event.height); c2.setBounds(b2); + if (isWin32) setLayoutDeferred(false); } } /** diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/os_custom.c b/bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/os_custom.c index 1e8ac836b8a..35eeeb0900b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/os_custom.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/os_custom.c @@ -439,7 +439,47 @@ JNIEXPORT jlong JNICALL OS_NATIVE(DPI_1AWARENESS_1CONTEXT_1UNAWARE_1GDISCALED) #else rc = 0; #endif - OS_NATIVE_EXIT(env, that, DPI_1AWARENESS_1CONTEXT_1UNAWARE_1GDISCALED_FUNC); + +#ifndef NO_DeferWindowPos__J_3J_3I_3I_3I_3I_3I +JNIEXPORT jlong JNICALL OS_NATIVE(DeferWindowPos__J_3J_3I_3I_3I_3I_3I) + (JNIEnv *env, jclass that, jlong arg0, jlongArray arg1, jintArray arg2, jintArray arg3, jintArray arg4, jintArray arg5, jintArray arg6) +{ + jlong *lparg1=NULL; + jint *lparg2=NULL; + jint *lparg3=NULL; + jint *lparg4=NULL; + jint *lparg5=NULL; + jint *lparg6=NULL; + jsize len = 0; + jlong rc = 0; + int i; + + OS_NATIVE_ENTER(env, that, DeferWindowPos__J_3J_3I_3I_3I_3I_3I_FUNC); + + if (arg1) if ((lparg1 = (*env)->GetLongArrayElements(env, arg1, NULL)) == NULL) goto fail; + if (arg2) if ((lparg2 = (*env)->GetIntArrayElements(env, arg2, NULL)) == NULL) goto fail; + if (arg3) if ((lparg3 = (*env)->GetIntArrayElements(env, arg3, NULL)) == NULL) goto fail; + if (arg4) if ((lparg4 = (*env)->GetIntArrayElements(env, arg4, NULL)) == NULL) goto fail; + if (arg5) if ((lparg5 = (*env)->GetIntArrayElements(env, arg5, NULL)) == NULL) goto fail; + if (arg6) if ((lparg6 = (*env)->GetIntArrayElements(env, arg6, NULL)) == NULL) goto fail; + + len = (*env)->GetArrayLength(env, arg1); + rc = arg0; + + for (i = 0; i < len; i++) { + if (rc == 0) break; + rc = (jlong)DeferWindowPos((HDWP)rc, (HWND)lparg1[i], (HWND)0, lparg2[i], lparg3[i], lparg4[i], lparg5[i], lparg6[i]); + } + +fail: + if (arg6 && lparg6) (*env)->ReleaseIntArrayElements(env, arg6, lparg6, 0); + if (arg5 && lparg5) (*env)->ReleaseIntArrayElements(env, arg5, lparg5, 0); + if (arg4 && lparg4) (*env)->ReleaseIntArrayElements(env, arg4, lparg4, 0); + if (arg3 && lparg3) (*env)->ReleaseIntArrayElements(env, arg3, lparg3, 0); + if (arg2 && lparg2) (*env)->ReleaseIntArrayElements(env, arg2, lparg2, 0); + if (arg1 && lparg1) (*env)->ReleaseLongArrayElements(env, arg1, lparg1, 0); + + OS_NATIVE_EXIT(env, that, DeferWindowPos__J_3J_3I_3I_3I_3I_3I_FUNC); return rc; } #endif diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/win32/OS.java b/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/win32/OS.java index 200c280610b..558e793e72c 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/win32/OS.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/win32/OS.java @@ -2532,6 +2532,8 @@ public static int HRESULT_FROM_WIN32(int x) { * @param hWndInsertAfter cast=(HWND) */ public static final native long DeferWindowPos (long hWinPosInfo, long hWnd, long hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags); +/** @method flags=no_gen */ +public static final native long DeferWindowPos (long hWinPosInfo, long[] hWnd, int[] X, int[] Y, int[] cx, int[] cy, int[] uFlags); /** * @param hWnd cast=(HWND) * @param wParam cast=(WPARAM) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Composite.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Composite.java index 10646461f88..1e34fd978b0 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Composite.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Composite.java @@ -977,13 +977,35 @@ boolean resizeChildren (boolean defer, WINDOWPOS [] pwp) { if (defer) { hdwp = OS.BeginDeferWindowPos (pwp.length); if (hdwp == 0) return false; - } - for (WINDOWPOS wp : pwp) { - if (wp != null) { - if (defer) { - hdwp = OS.DeferWindowPos (hdwp, wp.hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, wp.flags); - if (hdwp == 0) return false; - } else { + int count = 0; + for (WINDOWPOS wp : pwp) { + if (wp != null) count++; + } + if (count > 0) { + long [] hwnds = new long [count]; + int [] xs = new int [count]; + int [] ys = new int [count]; + int [] cxs = new int [count]; + int [] cys = new int [count]; + int [] flags = new int [count]; + int index = 0; + for (WINDOWPOS wp : pwp) { + if (wp != null) { + hwnds [index] = wp.hwnd; + xs [index] = wp.x; + ys [index] = wp.y; + cxs [index] = wp.cx; + cys [index] = wp.cy; + flags [index] = wp.flags; + index++; + } + } + hdwp = OS.DeferWindowPos (hdwp, hwnds, xs, ys, cxs, cys, flags); + if (hdwp == 0) return false; + } + } else { + for (WINDOWPOS wp : pwp) { + if (wp != null) { OS.SetWindowPos (wp.hwnd, 0, wp.x, wp.y, wp.cx, wp.cy, wp.flags); } } diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_CompositeDeferredLayout.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_CompositeDeferredLayout.java new file mode 100644 index 00000000000..ab1b488c461 --- /dev/null +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_CompositeDeferredLayout.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.tests.junit; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Shell; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class Test_CompositeDeferredLayout { + + public Shell shell; + public Composite composite; + + @BeforeEach + public void setUp() { + shell = new Shell(); + composite = new Composite(shell, SWT.NONE); + } + + @Test + public void test_deferredLayout_movesChildren() { + int numChildren = 5; + Button[] buttons = new Button[numChildren]; + for (int i = 0; i < numChildren; i++) { + buttons[i] = new Button(composite, SWT.PUSH); + buttons[i].setBounds(0, 0, 10, 10); + } + + composite.setLayoutDeferred(true); + + for (int i = 0; i < numChildren; i++) { + buttons[i].setBounds(10 * i, 10 * i, 20, 20); + } + + composite.setLayoutDeferred(false); + + for (int i = 0; i < numChildren; i++) { + Rectangle bounds = buttons[i].getBounds(); + assertEquals(new Rectangle(10 * i, 10 * i, 20, 20), bounds, "Button " + i + " bounds incorrect after deferred layout"); + } + } + + @Test + public void test_resizeChildren_batching() { + // This test indirectly targets the batching optimization in Composite.resizeChildren + // by verifying that a large number of children are moved correctly. + int numChildren = 100; + Button[] buttons = new Button[numChildren]; + for (int i = 0; i < numChildren; i++) { + buttons[i] = new Button(composite, SWT.PUSH); + buttons[i].setBounds(0, 0, 10, 10); + } + + composite.setLayoutDeferred(true); + + for (int i = 0; i < numChildren; i++) { + buttons[i].setBounds(i, i, 15, 15); + } + + // This call triggers resizeChildren with the deferred logic + composite.setLayoutDeferred(false); + + for (int i = 0; i < numChildren; i++) { + Rectangle bounds = buttons[i].getBounds(); + assertEquals(new Rectangle(i, i, 15, 15), bounds, "Button " + i + " bounds incorrect after large batch deferred layout"); + } + } +} diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_SashForm.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_SashForm.java new file mode 100644 index 00000000000..9fb3b1bd66e --- /dev/null +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_SashForm.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.tests.junit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Shell; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class Test_org_eclipse_swt_custom_SashForm { + + public Shell shell; + public SashForm sashForm; + + @BeforeEach + public void setUp() { + shell = new Shell(); + sashForm = new SashForm(shell, SWT.HORIZONTAL); + shell.setSize(400, 400); + } + + @Test + public void test_initialLayout() { + Button b1 = new Button(sashForm, SWT.PUSH); + Button b2 = new Button(sashForm, SWT.PUSH); + sashForm.setWeights(new int[] {50, 50}); + + shell.open(); // Trigger layout + + Rectangle bounds1 = b1.getBounds(); + Rectangle bounds2 = b2.getBounds(); + + assertTrue(bounds1.width > 0, "Button 1 width should be > 0"); + assertTrue(bounds2.width > 0, "Button 2 width should be > 0"); + assertEquals(bounds1.width, bounds2.width, 5, "Buttons should have approximately equal width"); // Allow small tolerance + } + + @Test + public void test_layoutDeferredDuringResize() { + // This test verifies that we can set layout deferred on SashForm + // (simulating what happens internally on Windows during drag) + // and that the layout updates correctly afterwards. + Button b1 = new Button(sashForm, SWT.PUSH); + Button b2 = new Button(sashForm, SWT.PUSH); + sashForm.setWeights(new int[] {50, 50}); + shell.open(); + + Rectangle initialBounds1 = b1.getBounds(); + + sashForm.setLayoutDeferred(true); + sashForm.setSize(600, 400); // Resize the sash form + sashForm.setLayoutDeferred(false); // Should trigger layout update + + Rectangle newBounds1 = b1.getBounds(); + assertTrue(newBounds1.width > initialBounds1.width, "Button 1 should have grown after SashForm resize"); + } +}