diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java index 9d5eacb8dd8..ee9b402004f 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java @@ -199,6 +199,7 @@ public class CTabFolder extends Composite { // close, min/max and chevron buttons boolean showClose = false; boolean showUnselectedClose = true; + boolean dirtyIndicatorStyle = false; boolean showMin = false; boolean minimized = false; @@ -1895,7 +1896,7 @@ public void run() { } } if (item != null) { - if (item.closeRect.contains(x,y)){ + if ((showClose || item.showClose) && item.closeRect.contains(x,y)){ item.closeImageState = SWT.SELECTED; redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); update(); @@ -1976,7 +1977,7 @@ public void run() { } } if (item != null) { - if (item.closeRect.contains(x,y)) { + if ((showClose || item.showClose) && item.closeRect.contains(x,y)) { boolean selected = item.closeImageState == SWT.SELECTED; item.closeImageState = SWT.HOT; redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); @@ -2798,7 +2799,7 @@ boolean setItemLocation(GC gc) { item.x = leftItemEdge; item.y = y; item.showing = true; - if (showClose || item.showClose) { + if (showClose || item.showClose || (dirtyIndicatorStyle && item.showDirty)) { item.closeRect.x = leftItemEdge - renderer.computeTrim(i, SWT.NONE, 0, 0, 0, 0).x; item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2; } @@ -2899,7 +2900,7 @@ boolean setItemSize(GC gc) { tab.height = tabHeight; tab.width = width; tab.closeRect.width = tab.closeRect.height = 0; - if (showClose || tab.showClose) { + if (showClose || tab.showClose || (dirtyIndicatorStyle && tab.showDirty)) { Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT); tab.closeRect.width = closeSize.x; tab.closeRect.height = closeSize.y; @@ -2983,8 +2984,8 @@ boolean setItemSize(GC gc) { tab.height = tabHeight; tab.width = width; tab.closeRect.width = tab.closeRect.height = 0; - if (showClose || tab.showClose) { - if (i == selectedIndex || showUnselectedClose) { + if (showClose || tab.showClose || (dirtyIndicatorStyle && tab.showDirty)) { + if (i == selectedIndex || showUnselectedClose || (dirtyIndicatorStyle && tab.showDirty)) { Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT); tab.closeRect.width = closeSize.x; tab.closeRect.height = closeSize.y; @@ -3669,6 +3670,55 @@ public void setUnselectedCloseVisible(boolean visible) { showUnselectedClose = visible; updateFolder(REDRAW); } +/** + * Sets whether the dirty indicator style is enabled. When enabled, + * dirty items (marked via {@link CTabItem#setShowDirty(boolean)}) show a + * bullet dot at the close button location instead of the traditional + * * prefix. The bullet transforms into the close button on hover + * when close is enabled for the folder or the individual item. + *

+ * Note: the dirty indicator is purely visual. Clicking the bullet does not + * close the tab unless close is independently enabled (via the + * {@link SWT#CLOSE} style or {@link CTabItem#setShowClose(boolean)}). + *

+ *

+ * The default value is false (traditional * prefix + * behavior). + *

+ * + * @param enabled true to enable the dirty indicator style + * + * @exception SWTException + * + * @see CTabItem#setShowDirty(boolean) + * @since 3.134 + */ +public void setDirtyIndicatorStyle(boolean enabled) { + checkWidget(); + if (dirtyIndicatorStyle == enabled) return; + dirtyIndicatorStyle = enabled; + updateFolder(REDRAW_TABS); +} +/** + * Returns whether the dirty indicator style is enabled. + * + * @return true if the dirty indicator style is enabled + * + * @exception SWTException + * + * @see #setDirtyIndicatorStyle(boolean) + * @since 3.134 + */ +public boolean getDirtyIndicatorStyle() { + checkWidget(); + return dirtyIndicatorStyle; +} /** * Specify whether the image appears on unselected tabs. * @@ -4003,8 +4053,10 @@ String _getToolTip(int x, int y) { CTabItem item = getItem(new Point (x, y)); if (item == null) return null; if (!item.showing) return null; - if ((showClose || item.showClose) && item.closeRect.contains(x, y)) { - return SWT.getMessage("SWT_Close"); //$NON-NLS-1$ + if (item.closeRect.contains(x, y)) { + if (showClose || item.showClose) { + return SWT.getMessage("SWT_Close"); //$NON-NLS-1$ + } } return item.getToolTipText(); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java index 3c6cfbc86b2..a0af55bfc30 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java @@ -265,7 +265,7 @@ protected Point computeSize (int part, int state, GC gc, int wHint, int hHint) { if (shouldApplyLargeTextPadding(parent)) { width += getLargeTextPadding(item) * 2; - } else if (shouldDrawCloseIcon(item)) { + } else if (shouldAllocateCloseRect(item)) { if (width > 0) width += INTERNAL_SPACING; width += computeSize(PART_CLOSE_BUTTON, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT).x; } @@ -285,6 +285,15 @@ private boolean shouldDrawCloseIcon(CTabItem item) { return showClose && isSelectedOrShowCloseForUnselected; } + private boolean shouldDrawDirtyIndicator(CTabItem item) { + CTabFolder folder = item.getParent(); + return folder.dirtyIndicatorStyle && item.showDirty; + } + + private boolean shouldAllocateCloseRect(CTabItem item) { + return shouldDrawCloseIcon(item) || shouldDrawDirtyIndicator(item); + } + /** * Returns padding for the text of a tab item when showing images is disabled for the tab folder. */ @@ -683,8 +692,21 @@ void drawBody(GC gc, Rectangle bounds, int state) { } void drawClose(GC gc, Rectangle closeRect, int closeImageState) { + drawClose(gc, closeRect, closeImageState, false); + } + + void drawClose(GC gc, Rectangle closeRect, int closeImageState, boolean showDirtyIndicator) { if (closeRect.width == 0 || closeRect.height == 0) return; + // When dirty and not hovered/pressed, draw bullet instead of X + if (showDirtyIndicator) { + int maskedState = closeImageState & (SWT.HOT | SWT.SELECTED | SWT.BACKGROUND); + if (maskedState != SWT.HOT && maskedState != SWT.SELECTED) { + drawDirtyIndicator(gc, closeRect); + return; + } + } + // draw X with length of this constant final int lineLength = 8; int x = closeRect.x + Math.max(1, (closeRect.width-lineLength)/2); @@ -715,6 +737,16 @@ void drawClose(GC gc, Rectangle closeRect, int closeImageState) { gc.setForeground(originalForeground); } + private void drawDirtyIndicator(GC gc, Rectangle closeRect) { + int diameter = 8; + int x = closeRect.x + (closeRect.width - diameter) / 2; + int y = closeRect.y + (closeRect.height - diameter) / 2; + Color originalBackground = gc.getBackground(); + gc.setBackground(gc.getForeground()); + gc.fillOval(x, y, diameter, diameter); + gc.setBackground(originalBackground); + } + private void drawCloseLines(GC gc, int x, int y, int lineLength, boolean hot) { if (hot) { gc.setLineWidth(gc.getLineWidth() + 2); @@ -1061,7 +1093,7 @@ void drawSelected(int itemIndex, GC gc, Rectangle bounds, int state ) { // draw Image Rectangle trim = computeTrim(itemIndex, SWT.NONE, 0, 0, 0, 0); int xDraw = x - trim.x; - if (parent.single && shouldDrawCloseIcon(item)) xDraw += item.closeRect.width; + if (parent.single && shouldAllocateCloseRect(item)) xDraw += item.closeRect.width; Image image = item.getImage(); if (image != null && !image.isDisposed() && parent.showSelectedImage) { Rectangle imageBounds = image.getBounds(); @@ -1109,7 +1141,9 @@ void drawSelected(int itemIndex, GC gc, Rectangle bounds, int state ) { gc.setBackground(orginalBackground); } } - if (shouldDrawCloseIcon(item)) drawClose(gc, item.closeRect, item.closeImageState); + if (shouldAllocateCloseRect(item)) { + drawClose(gc, item.closeRect, item.closeImageState, shouldDrawDirtyIndicator(item)); + } } } @@ -1117,7 +1151,7 @@ private int getLeftTextMargin(CTabItem item) { int margin = 0; if (shouldApplyLargeTextPadding(parent)) { margin += getLargeTextPadding(item); - if (shouldDrawCloseIcon(item)) { + if (shouldAllocateCloseRect(item)) { margin -= item.closeRect.width / 2; } } @@ -1248,7 +1282,7 @@ void drawUnselected(int index, GC gc, Rectangle bounds, int state) { Rectangle imageBounds = image.getBounds(); // only draw image if it won't overlap with close button int maxImageWidth = x + width - xDraw - (trim.width + trim.x); - if (shouldDrawCloseIcon(item)) { + if (shouldAllocateCloseRect(item)) { maxImageWidth -= item.closeRect.width + INTERNAL_SPACING; } if (imageBounds.width < maxImageWidth) { @@ -1264,7 +1298,7 @@ void drawUnselected(int index, GC gc, Rectangle bounds, int state) { // draw Text xDraw += getLeftTextMargin(item); int textWidth = x + width - xDraw - (trim.width + trim.x); - if (shouldDrawCloseIcon(item)) { + if (shouldAllocateCloseRect(item)) { textWidth -= item.closeRect.width + INTERNAL_SPACING; } if (textWidth > 0) { @@ -1281,8 +1315,10 @@ void drawUnselected(int index, GC gc, Rectangle bounds, int state) { gc.drawText(item.shortenedText, xDraw, textY, FLAGS); gc.setFont(gcFont); } - // draw close - if (shouldDrawCloseIcon(item)) drawClose(gc, item.closeRect, item.closeImageState); + // draw close or dirty indicator + if (shouldAllocateCloseRect(item)) { + drawClose(gc, item.closeRect, item.closeImageState, shouldDrawDirtyIndicator(item)); + } } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java index 24aa3e17725..895afaef505 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java @@ -55,6 +55,7 @@ public class CTabItem extends Item { int closeImageState = SWT.BACKGROUND; int state = SWT.NONE; boolean showClose = false; + boolean showDirty = false; boolean showing = false; /** @@ -276,6 +277,26 @@ public boolean getShowClose() { checkWidget(); return showClose; } +/** + * Returns true to indicate that the receiver is dirty + * (has unsaved changes). When the parent folder's dirty indicator style + * is enabled, dirty items show a bullet dot at the close button location + * instead of the default * prefix. + * + * @return true if the item is marked as dirty + * + * @exception SWTException + * + * @see CTabFolder#setDirtyIndicatorStyle(boolean) + * @since 3.134 + */ +public boolean getShowDirty() { + checkWidget(); + return showDirty; +} /** * Returns the receiver's tool tip text, or null if it has * not been set. @@ -490,6 +511,29 @@ public void setShowClose(boolean close) { showClose = close; parent.updateFolder(CTabFolder.REDRAW_TABS); } +/** + * Marks this item as dirty (having unsaved changes). When the parent + * folder's dirty indicator style is enabled via + * {@link CTabFolder#setDirtyIndicatorStyle(boolean)}, dirty items + * show a bullet dot at the close button location. The bullet transforms + * into the close button on hover. + * + * @param dirty true to mark the item as dirty + * + * @exception SWTException + * + * @see CTabFolder#setDirtyIndicatorStyle(boolean) + * @since 3.134 + */ +public void setShowDirty(boolean dirty) { + checkWidget(); + if (showDirty == dirty) return; + showDirty = dirty; + parent.updateFolder(CTabFolder.REDRAW_TABS); +} /** * Sets the text to display on the tab. * A carriage return '\n' allows to display multi line text. diff --git a/examples/org.eclipse.swt.snippets/Snippets.md b/examples/org.eclipse.swt.snippets/Snippets.md index d9df7eaa3e4..b40544f3990 100644 --- a/examples/org.eclipse.swt.snippets/Snippets.md +++ b/examples/org.eclipse.swt.snippets/Snippets.md @@ -118,6 +118,7 @@ To contribute a new snippet, [create a snippet contribution as a pull request](h - [prevent an item from closing](https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet82.java) – [(preview)](https://github.com/eclipse-platform/eclipse.platform.swt/blob/master/examples/org.eclipse.swt.snippets/previews/Snippet82.png "Preview for Snippet 82") - [min and max buttons, close button and image only on selected tab](https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet165.java) – [(preview)](https://github.com/eclipse-platform/eclipse.platform.swt/blob/master/examples/org.eclipse.swt.snippets/previews/Snippet165.png "Preview for Snippet 165") - [demonstration of a multi line CTabFolder](https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet371.java) – [(preview)](https://github.com/eclipse-platform/eclipse.platform.swt/blob/master/examples/org.eclipse.swt.snippets/previews/Snippet371.png "Preview for Snippet 371") +- [dirty indicator using bullet dot on close button with theme switching](https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet394.java) ### **Cursor** - [set the hand cursor into a control](https://github.com/eclipse-platform/eclipse.platform.swt/tree/master/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet44.java) – [(preview)](https://github.com/eclipse-platform/eclipse.platform.swt/blob/master/examples/org.eclipse.swt.snippets/previews/Snippet44.png "Preview for Snippet 44") diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet394.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet394.java new file mode 100644 index 00000000000..132843a49e1 --- /dev/null +++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet394.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * 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 + *******************************************************************************/ +package org.eclipse.swt.snippets; + +/* + * CTabFolder example: dirty indicator using bullet dot on close button, + * with runtime dark/light theme switching to show color adaptation. + * + * For a list of all SWT example snippets see + * http://www.eclipse.org/swt/snippets/ + */ +import org.eclipse.swt.*; +import org.eclipse.swt.custom.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +public class Snippet394 { + public static void main(String[] args) { + Display display = new Display(); + Shell shell = new Shell(display); + shell.setLayout(new GridLayout()); + shell.setText("CTabFolder Dirty Indicator"); + + CTabFolder folder = new CTabFolder(shell, SWT.CLOSE | SWT.BORDER); + folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + folder.setDirtyIndicatorStyle(true); + + for (int i = 0; i < 4; i++) { + CTabItem item = new CTabItem(folder, SWT.NONE); + item.setText("Tab " + i); + Text text = new Text(folder, SWT.MULTI | SWT.WRAP); + text.setText("Content for tab " + i); + item.setControl(text); + } + + // Mark tabs 0 and 2 as dirty + folder.getItem(0).setShowDirty(true); + folder.getItem(2).setShowDirty(true); + folder.setSelection(0); + + Button toggleButton = new Button(shell, SWT.PUSH); + toggleButton.setText("Toggle dirty on selected tab"); + toggleButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + toggleButton.addListener(SWT.Selection, e -> { + CTabItem selected = folder.getSelection(); + if (selected != null) { + selected.setShowDirty(!selected.getShowDirty()); + } + }); + + boolean[] isDark = {false}; + Button toggleThemeButton = new Button(shell, SWT.PUSH); + toggleThemeButton.setText("Switch to Dark Theme"); + toggleThemeButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + toggleThemeButton.addListener(SWT.Selection, e -> { + isDark[0] = !isDark[0]; + if (isDark[0]) { + Color tabBg = new Color(display, 43, 43, 43); + Color selBg = new Color(display, 60, 63, 65); + Color contentBg = new Color(display, 30, 30, 30); + Color fg = new Color(display, 187, 187, 187); + folder.setBackground(tabBg); + folder.setForeground(fg); + folder.setSelectionBackground(selBg); + folder.setSelectionForeground(fg); + for (int i = 0; i < folder.getItemCount(); i++) { + Control ctrl = folder.getItem(i).getControl(); + if (ctrl != null) { + ctrl.setBackground(contentBg); + ctrl.setForeground(fg); + } + } + toggleThemeButton.setText("Switch to Light Theme"); + } else { + folder.setBackground(null); + folder.setForeground(null); + folder.setSelectionBackground((Color) null); + folder.setSelectionForeground(null); + for (int i = 0; i < folder.getItemCount(); i++) { + Control ctrl = folder.getItem(i).getControl(); + if (ctrl != null) { + ctrl.setBackground(null); + ctrl.setForeground(null); + } + } + toggleThemeButton.setText("Switch to Dark Theme"); + } + }); + + shell.setSize(400, 300); + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + display.dispose(); + } +} diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabFolder.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabFolder.java index 6c83fe0c4c2..afb4e98f6a6 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabFolder.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabFolder.java @@ -939,4 +939,114 @@ public void test_getItem_byPoint() { assertNull(notFound); } +@Test +public void test_dirtyIndicator_doesNotCloseWithoutCloseEnabled() { + // Create folder WITHOUT SWT.CLOSE style + makeCleanEnvironment(SWT.NONE); + shell.setLayout(new FillLayout()); + + for (int i = 0; i < 3; i++) { + CTabItem item = new CTabItem(ctabFolder, SWT.NONE); + item.setText("Tab " + i); + Label content = new Label(ctabFolder, SWT.NONE); + content.setText("Content " + i); + item.setControl(content); + } + + ctabFolder.setDirtyIndicatorStyle(true); + ctabFolder.getItem(0).setShowDirty(true); + ctabFolder.setSelection(0); + shell.setSize(800, 400); + SwtTestUtil.openShell(shell); + processEvents(); + + // The dirty item should still have a closeRect allocated for the bullet + CTabItem dirtyItem = ctabFolder.getItem(0); + + int itemCountBefore = ctabFolder.getItemCount(); + assertEquals(3, itemCountBefore); + + // Simulate mouse down + up on the closeRect area via events + // The item should NOT be disposed because close is not enabled + Rectangle itemBounds = dirtyItem.getBounds(); + Event mouseDown = new Event(); + mouseDown.type = SWT.MouseDown; + mouseDown.button = 1; + mouseDown.x = itemBounds.x + itemBounds.width - 5; + mouseDown.y = itemBounds.y + itemBounds.height / 2; + ctabFolder.notifyListeners(SWT.MouseDown, mouseDown); + processEvents(); + + Event mouseUp = new Event(); + mouseUp.type = SWT.MouseUp; + mouseUp.button = 1; + mouseUp.x = mouseDown.x; + mouseUp.y = mouseDown.y; + ctabFolder.notifyListeners(SWT.MouseUp, mouseUp); + processEvents(); + + // Item should still exist - dirty indicator click should not close the tab + assertFalse(dirtyItem.isDisposed(), "Dirty item should not be disposed when close is not enabled"); + assertEquals(3, ctabFolder.getItemCount(), "Item count should remain unchanged"); +} + +@Test +public void test_dirtyIndicator_closesWhenCloseEnabled() { + // Create folder WITH SWT.CLOSE style + makeCleanEnvironment(SWT.CLOSE); + shell.setLayout(new FillLayout()); + + for (int i = 0; i < 3; i++) { + CTabItem item = new CTabItem(ctabFolder, SWT.NONE); + item.setText("Tab " + i); + Label content = new Label(ctabFolder, SWT.NONE); + content.setText("Content " + i); + item.setControl(content); + } + + ctabFolder.setDirtyIndicatorStyle(true); + ctabFolder.getItem(0).setShowDirty(true); + ctabFolder.setSelection(0); + shell.setSize(800, 400); + SwtTestUtil.openShell(shell); + processEvents(); + + CTabItem dirtyItem = ctabFolder.getItem(0); + + // Access closeRect via reflection to click precisely on it + try { + Field closeRectField = CTabItem.class.getDeclaredField("closeRect"); + closeRectField.setAccessible(true); + Rectangle closeRectValue = (Rectangle) closeRectField.get(dirtyItem); + + // closeRect should be allocated (non-zero) for dirty items + assertTrue(closeRectValue.width > 0 && closeRectValue.height > 0, + "closeRect should be allocated for dirty item"); + + // Simulate mouse down on the close rect + Event mouseDown = new Event(); + mouseDown.type = SWT.MouseDown; + mouseDown.button = 1; + mouseDown.x = closeRectValue.x + closeRectValue.width / 2; + mouseDown.y = closeRectValue.y + closeRectValue.height / 2; + ctabFolder.notifyListeners(SWT.MouseDown, mouseDown); + processEvents(); + + // Simulate mouse up on the same location + Event mouseUp = new Event(); + mouseUp.type = SWT.MouseUp; + mouseUp.button = 1; + mouseUp.x = mouseDown.x; + mouseUp.y = mouseDown.y; + ctabFolder.notifyListeners(SWT.MouseUp, mouseUp); + processEvents(); + + // Item should be disposed - close is enabled via SWT.CLOSE + assertTrue(dirtyItem.isDisposed(), "Dirty item should be disposed when close is enabled"); + assertEquals(2, ctabFolder.getItemCount(), "Item count should decrease by 1"); + } catch (NoSuchFieldException | IllegalAccessException e) { + fail("Failed to access closeRect via reflection: " + e.getMessage()); + } +} + } diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabItem.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabItem.java index 25fe59923ae..5ecb72fe6bd 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabItem.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabItem.java @@ -14,6 +14,8 @@ package org.eclipse.swt.tests.junit; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; @@ -76,4 +78,22 @@ public void test_setSelectionForegroundLorg_eclipse_swt_graphics_Color() { cTabItem.setSelectionForeground(null); assertEquals(red, cTabItem.getSelectionForeground()); } -} \ No newline at end of file + +@Test +public void test_setShowDirty() { + assertFalse(cTabItem.getShowDirty()); + cTabItem.setShowDirty(true); + assertTrue(cTabItem.getShowDirty()); + cTabItem.setShowDirty(false); + assertFalse(cTabItem.getShowDirty()); +} + +@Test +public void test_dirtyIndicatorCloseStyle() { + assertFalse(cTabFolder.getDirtyIndicatorStyle()); + cTabFolder.setDirtyIndicatorStyle(true); + assertTrue(cTabFolder.getDirtyIndicatorStyle()); + cTabFolder.setDirtyIndicatorStyle(false); + assertFalse(cTabFolder.getDirtyIndicatorStyle()); +} +}