diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java index 48d78d9b565..0e20b8175d3 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Link.java @@ -627,6 +627,17 @@ LRESULT WM_CHAR (long wParam, long lParam) { return result; } +@Override +LRESULT WM_ERASEBKGND (long wParam, long lParam) { + LRESULT result = super.WM_ERASEBKGND (wParam, lParam); + if (result != null) return result; + /* + * Feature in Windows. The SysLink control flashes when resized. + * The fix is to prevent the background from being erased. + */ + return LRESULT.ONE; +} + @Override LRESULT WM_GETDLGCODE (long wParam, long lParam) { long code = callWindowProc (handle, OS.WM_GETDLGCODE, wParam, lParam); diff --git a/tests/org.eclipse.swt.tests.win32/JUnit Tests/org/eclipse/swt/tests/win32/widgets/Test_org_eclipse_swt_widgets_Link.java b/tests/org.eclipse.swt.tests.win32/JUnit Tests/org/eclipse/swt/tests/win32/widgets/Test_org_eclipse_swt_widgets_Link.java new file mode 100644 index 00000000000..3a29416f46b --- /dev/null +++ b/tests/org.eclipse.swt.tests.win32/JUnit Tests/org/eclipse/swt/tests/win32/widgets/Test_org_eclipse_swt_widgets_Link.java @@ -0,0 +1,128 @@ +package org.eclipse.swt.tests.win32.widgets; + +import static org.junit.jupiter.api.Assertions.*; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Shell; +import org.junit.jupiter.api.Test; + +public class Test_org_eclipse_swt_widgets_Link { + + @Test + public void test_LinkRendersCorrectlyAfterResize() { + Display display = new Display(); + try { + Shell shell = new Shell(display); + shell.setSize(400, 300); + Link link = new Link(shell, SWT.NONE); + link.setText("Visit Eclipse website"); + link.setBounds(10, 10, 200, 50); + + shell.open(); + + // Force initial paint + while (display.readAndDispatch()) { + // loop until no more events + } + + // Resize multiple times rapidly (stress test for flicker) + for (int i = 0; i < 10; i++) { + link.setSize(200 + (i * 10), 50 + (i * 5)); + while (display.readAndDispatch()) { + // loop until no more events + } + } + + // Verify link is still functional and sized correctly + Point size = link.getSize(); + assertTrue(size.x > 0 && size.y > 0, "Link should have valid size after resize"); + assertEquals("Visit Eclipse website", + link.getText(), "Link text should be preserved after resize"); + + // Verify the link is still visible and has proper bounds + Rectangle bounds = link.getBounds(); + assertTrue(bounds.width > 0 && bounds.height > 0, + "Link should have valid bounds after multiple resizes"); + + shell.close(); + } finally { + display.dispose(); + } + } + + @Test + public void test_LinkBackgroundNotErasedDuringPaint() { + Display display = new Display(); + try { + Shell shell = new Shell(display); + Link link = new Link(shell, SWT.NONE); + link.setText("Test link"); + + final int[] paintCount = {0}; + final int[] eraseCount = {0}; + + // Monitor paint events + link.addListener(SWT.Paint, e -> paintCount[0]++); + link.addListener(SWT.EraseItem, e -> eraseCount[0]++); + + shell.setSize(300, 200); + shell.open(); + + // Trigger redraws + for (int i = 0; i < 5; i++) { + link.redraw(); + while (display.readAndDispatch()) { + // loop until no more events + } + } + + // We should see paints, but minimal erase operations + assertTrue(paintCount[0] > 0, "Link should have been painted"); + // Note: EraseItem may not fire for Link widget, this is platform-specific + + shell.close(); + } finally { + display.dispose(); + } + } + + @Test + public void test_LinkMaintainsContentDuringRapidResize() { + Display display = new Display(); + try { + Shell shell = new Shell(display); + shell.setSize(400, 300); + Link link = new Link(shell, SWT.NONE); + String testText = "Click here or there"; + link.setText(testText); + link.setBounds(10, 10, 300, 100); + + shell.open(); + while (display.readAndDispatch()) { + // loop until no more events + } + + // Rapidly resize - this would cause flickering in the old implementation + for (int i = 0; i < 20; i++) { + int newWidth = 200 + (i % 2) * 100; + int newHeight = 50 + (i % 2) * 30; + link.setSize(newWidth, newHeight); + + // Process events but don't wait for full redraw + display.readAndDispatch(); + + // Content should remain intact + assertEquals(testText, link.getText(), + "Link text should remain unchanged during resize iteration " + i); + } + + shell.close(); + } finally { + display.dispose(); + } + } +} \ No newline at end of file diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Link.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Link.java index 0159400a080..2bb30e8a30e 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Link.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Link.java @@ -24,6 +24,9 @@ import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Link; import org.junit.jupiter.api.BeforeEach; @@ -62,6 +65,7 @@ public void test_ConstructorLorg_eclipse_swt_widgets_CompositeI() { fail("No exception thrown for parent == null"); } catch (IllegalArgumentException e) { + // expected } } @@ -82,6 +86,7 @@ public void widgetDefaultSelected(SelectionEvent e) { link.addSelectionListener(null); fail("No exception thrown for addSelectionListener with null argument"); } catch (IllegalArgumentException e) { + // expected } link.addSelectionListener(listener); @@ -92,6 +97,7 @@ public void widgetDefaultSelected(SelectionEvent e) { link.removeSelectionListener(null); fail("No exception thrown for removeSelectionListener with null argument"); } catch (IllegalArgumentException e) { + // expected } listenerCalled = false; link.removeSelectionListener(listener); @@ -172,6 +178,7 @@ public void test_setTextLjava_lang_String() { link.setText(null); fail("No exception thrown for text == null"); } catch (IllegalArgumentException e) { + // expected } } @@ -184,4 +191,90 @@ public void test_setLinkForegroundLorg_eclipse_swt_graphics_Color() { link.setLinkForeground(null); assertNotEquals(color, link.getForeground()); } + + +@Test +public void test_LinkRendersCorrectlyAfterResize() { + shell.setSize(400, 300); + link.setText("Visit Eclipse website"); + link.setBounds(10, 10, 200, 50); + + shell.open(); + + Display display = shell.getDisplay(); + // Force initial paint + while (display.readAndDispatch()) { + // loop until no more events + } + + // Resize multiple times rapidly (stress test for flicker) + for (int i = 0; i < 10; i++) { + link.setSize(200 + (i * 10), 50 + (i * 5)); + while (display.readAndDispatch()) { + // loop until no more events + } + } + + // Verify link is still functional and sized correctly + Point size = link.getSize(); + assertTrue(size.x > 0 && size.y > 0, "Link should have valid size after resize"); + assertEquals("Visit Eclipse website", + link.getText(), "Link text should be preserved after resize"); + + // Verify the link is still visible and has proper bounds + Rectangle bounds = link.getBounds(); + assertTrue(bounds.width > 0 && bounds.height > 0, + "Link should have valid bounds after multiple resizes"); +} + +@Test +public void test_LinkBackgroundNotErasedDuringPaint() { + link.setText("Test link"); + + shell.setSize(300, 200); + link.setVisible(true); + shell.open(); + + Display display = shell.getDisplay(); + // Trigger redraws and ensure no crashes or obvious rendering failure + for (int i = 0; i < 10; i++) { + link.redraw(); + link.update(); + while (display.readAndDispatch()) { + // loop until no more events + } + } + + // Verify the link is still valid and has correct text + assertFalse(link.isDisposed(), "Link should not be disposed after multiple redraws"); + assertEquals("Test link", link.getText(), "Link text should be intact"); +} + +@Test +public void test_LinkMaintainsContentDuringRapidResize() { + shell.setSize(400, 300); + String testText = "Click here or there"; + link.setText(testText); + link.setBounds(10, 10, 300, 100); + + shell.open(); + Display display = shell.getDisplay(); + while (display.readAndDispatch()) { + // loop until no more events + } + + // Rapidly resize - this would cause flickering in the old implementation + for (int i = 0; i < 20; i++) { + int newWidth = 200 + (i % 2) * 100; + int newHeight = 50 + (i % 2) * 30; + link.setSize(newWidth, newHeight); + + // Process events but don't wait for full redraw + display.readAndDispatch(); + + // Content should remain intact + assertEquals(testText, link.getText(), + "Link text should remain unchanged during resize iteration " + i); + } +} }