From 0f590d7a63826c2e78f32e3fa27cac129940e730 Mon Sep 17 00:00:00 2001
From: Lars Vogel
Date: Thu, 19 Mar 2026 16:30:00 +0100
Subject: [PATCH 1/3] Add dirty indicator bullet style for CTabFolder tabs
When enabled via CTabFolder.setDirtyIndicatorCloseStyle(true), dirty
tabs show a filled circle at the close button location instead of the
traditional '*' prefix. The bullet transforms into the close button on
hover, matching the behavior of VS Code and similar editors.
This is opt-in (disabled by default) to preserve backward compatibility.
The feature adds:
- CTabFolder.setDirtyIndicatorCloseStyle(boolean) / getDirtyIndicatorCloseStyle()
- CTabItem.setShowDirty(boolean) / getShowDirty()
- Rendering via fillOval for cross-platform consistency
- Snippet391 demonstrating the feature
Based on the approach from PR #1632 by schneidermic0, with fixes for
the copy-paste bug, preference toggle support per PMC request, and
fillOval rendering instead of drawString for pixel-perfect results.
See: https://github.com/eclipse-platform/eclipse.platform.swt/pull/1632
Co-Authored-By: Claude Opus 4.6
---
.../org/eclipse/swt/custom/CTabFolder.java | 55 ++++++++++++++--
.../swt/custom/CTabFolderRenderer.java | 53 ++++++++++++---
.../org/eclipse/swt/custom/CTabItem.java | 44 +++++++++++++
.../org/eclipse/swt/snippets/Snippet393.java | 66 +++++++++++++++++++
.../Test_org_eclipse_swt_custom_CTabItem.java | 20 ++++++
5 files changed, 225 insertions(+), 13 deletions(-)
create mode 100644 examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java
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 23717891c7b..fb9751b4f64 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;
@@ -2791,7 +2792,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;
}
@@ -2892,7 +2893,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;
@@ -2976,8 +2977,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;
@@ -3662,6 +3663,50 @@ public void setUnselectedCloseVisible(boolean visible) {
showUnselectedClose = visible;
updateFolder(REDRAW);
}
+/**
+ * Sets whether the dirty indicator uses the close button style. 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.
+ *
+ * The default value is false (traditional * prefix
+ * behavior).
+ *
+ *
+ * @param useCloseButtonStyle true to use the bullet-on-close-button
+ * style for dirty indicators
+ *
+ * @exception SWTException
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
+ *
+ *
+ * @see CTabItem#setShowDirty(boolean)
+ * @since 3.134
+ */
+public void setDirtyIndicatorCloseStyle(boolean useCloseButtonStyle) {
+ checkWidget();
+ if (dirtyIndicatorStyle == useCloseButtonStyle) return;
+ dirtyIndicatorStyle = useCloseButtonStyle;
+ updateFolder(REDRAW_TABS);
+}
+/**
+ * Returns whether the dirty indicator uses the close button style.
+ *
+ * @return true if the dirty indicator uses the close button style
+ *
+ * @exception SWTException
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
+ *
+ *
+ * @see #setDirtyIndicatorCloseStyle(boolean)
+ * @since 3.134
+ */
+public boolean getDirtyIndicatorCloseStyle() {
+ checkWidget();
+ return dirtyIndicatorStyle;
+}
/**
* Specify whether the image appears on unselected tabs.
*
@@ -3996,7 +4041,7 @@ 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)) {
+ if ((showClose || item.showClose || (dirtyIndicatorStyle && item.showDirty)) && item.closeRect.contains(x, y)) {
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 b0908703e81..d765689481d 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.
*/
@@ -692,8 +701,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);
@@ -724,6 +746,17 @@ 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;
+ y += parent.onBottom ? -1 : 1;
+ 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);
@@ -1070,7 +1103,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();
@@ -1118,7 +1151,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));
+ }
}
}
@@ -1126,7 +1161,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;
}
}
@@ -1264,7 +1299,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) {
@@ -1280,7 +1315,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) {
@@ -1297,8 +1332,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..2b4f518ffb2 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
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
+ *
+ *
+ * @see CTabFolder#setDirtyIndicatorCloseStyle(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#setDirtyIndicatorCloseStyle(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
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
+ *
+ *
+ * @see CTabFolder#setDirtyIndicatorCloseStyle(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/src/org/eclipse/swt/snippets/Snippet393.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java
new file mode 100644
index 00000000000..07d922d6dd9
--- /dev/null
+++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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
+ *
+ * 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.layout.*;
+import org.eclipse.swt.widgets.*;
+
+public class Snippet393 {
+ 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.setDirtyIndicatorCloseStyle(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());
+ }
+ });
+
+ 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_CTabItem.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabItem.java
index 25fe59923ae..3f6e9e201cd 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());
}
+
+@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.getDirtyIndicatorCloseStyle());
+ cTabFolder.setDirtyIndicatorCloseStyle(true);
+ assertTrue(cTabFolder.getDirtyIndicatorCloseStyle());
+ cTabFolder.setDirtyIndicatorCloseStyle(false);
+ assertFalse(cTabFolder.getDirtyIndicatorCloseStyle());
+}
}
\ No newline at end of file
From 74d48e30cdab5bbdddc1884ddc7ef44ffcc21b88 Mon Sep 17 00:00:00 2001
From: Lars Vogel
Date: Thu, 26 Mar 2026 11:42:53 +0100
Subject: [PATCH 2/3] Fix dirty indicator pixel offset, add theme-switching
snippet, fix test EOF
- Remove spurious y+1/-1 offset in drawDirtyIndicator; the oval is
already centered from closeRect bounds, unlike the X drawing which
needs the nudge due to its Math.max(1,...) coordinate logic
- Extend Snippet393 with a dark/light theme toggle button to demonstrate
that the dirty bullet color adapts to the tab foreground color
- Add missing newline at end of Test_org_eclipse_swt_custom_CTabItem.java
Co-Authored-By: Claude Sonnet 4.6
---
.../swt/custom/CTabFolderRenderer.java | 1 -
.../org/eclipse/swt/snippets/Snippet393.java | 43 ++++++++++++++++++-
.../Test_org_eclipse_swt_custom_CTabItem.java | 2 +-
3 files changed, 43 insertions(+), 3 deletions(-)
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 d765689481d..fa5c72407b8 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
@@ -750,7 +750,6 @@ 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;
- y += parent.onBottom ? -1 : 1;
Color originalBackground = gc.getBackground();
gc.setBackground(gc.getForeground());
gc.fillOval(x, y, diameter, diameter);
diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java
index 07d922d6dd9..eaded3f5337 100644
--- a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java
+++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java
@@ -11,13 +11,15 @@
package org.eclipse.swt.snippets;
/*
- * CTabFolder example: dirty indicator using bullet dot on close button
+ * 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.*;
@@ -55,6 +57,45 @@ public static void main(String[] args) {
}
});
+ 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()) {
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 3f6e9e201cd..baec16653b3 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
@@ -96,4 +96,4 @@ public void test_dirtyIndicatorCloseStyle() {
cTabFolder.setDirtyIndicatorCloseStyle(false);
assertFalse(cTabFolder.getDirtyIndicatorCloseStyle());
}
-}
\ No newline at end of file
+}
From 6212db051f373f0fdfbd54e814487e6908957d71 Mon Sep 17 00:00:00 2001
From: Lars Vogel
Date: Thu, 26 Mar 2026 12:00:21 +0100
Subject: [PATCH 3/3] Rename setDirtyIndicatorCloseStyle to
setDirtyIndicatorStyle
"CloseStyle" leaks the implementation detail that the bullet occupies
the close button area. The shorter name describes the feature, not
the mechanism.
Co-Authored-By: Claude Sonnet 4.6
---
.../org/eclipse/swt/custom/CTabFolder.java | 19 +++++++++----------
.../org/eclipse/swt/custom/CTabItem.java | 6 +++---
.../org/eclipse/swt/snippets/Snippet393.java | 2 +-
.../Test_org_eclipse_swt_custom_CTabItem.java | 10 +++++-----
4 files changed, 18 insertions(+), 19 deletions(-)
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 fb9751b4f64..50b089f99b7 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
@@ -3664,7 +3664,7 @@ public void setUnselectedCloseVisible(boolean visible) {
updateFolder(REDRAW);
}
/**
- * Sets whether the dirty indicator uses the close button style. When enabled,
+ * 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.
@@ -3673,8 +3673,7 @@ public void setUnselectedCloseVisible(boolean visible) {
* behavior).
*
*
- * @param useCloseButtonStyle true to use the bullet-on-close-button
- * style for dirty indicators
+ * @param enabled true to enable the dirty indicator style
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
@@ -3684,26 +3683,26 @@ public void setUnselectedCloseVisible(boolean visible) {
* @see CTabItem#setShowDirty(boolean)
* @since 3.134
*/
-public void setDirtyIndicatorCloseStyle(boolean useCloseButtonStyle) {
+public void setDirtyIndicatorStyle(boolean enabled) {
checkWidget();
- if (dirtyIndicatorStyle == useCloseButtonStyle) return;
- dirtyIndicatorStyle = useCloseButtonStyle;
+ if (dirtyIndicatorStyle == enabled) return;
+ dirtyIndicatorStyle = enabled;
updateFolder(REDRAW_TABS);
}
/**
- * Returns whether the dirty indicator uses the close button style.
+ * Returns whether the dirty indicator style is enabled.
*
- * @return true if the dirty indicator uses the close button style
+ * @return true if the dirty indicator style is enabled
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
- * @see #setDirtyIndicatorCloseStyle(boolean)
+ * @see #setDirtyIndicatorStyle(boolean)
* @since 3.134
*/
-public boolean getDirtyIndicatorCloseStyle() {
+public boolean getDirtyIndicatorStyle() {
checkWidget();
return dirtyIndicatorStyle;
}
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 2b4f518ffb2..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
@@ -290,7 +290,7 @@ public boolean getShowClose() {
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
- * @see CTabFolder#setDirtyIndicatorCloseStyle(boolean)
+ * @see CTabFolder#setDirtyIndicatorStyle(boolean)
* @since 3.134
*/
public boolean getShowDirty() {
@@ -514,7 +514,7 @@ public void setShowClose(boolean close) {
/**
* Marks this item as dirty (having unsaved changes). When the parent
* folder's dirty indicator style is enabled via
- * {@link CTabFolder#setDirtyIndicatorCloseStyle(boolean)}, dirty items
+ * {@link CTabFolder#setDirtyIndicatorStyle(boolean)}, dirty items
* show a bullet dot at the close button location. The bullet transforms
* into the close button on hover.
*
@@ -525,7 +525,7 @@ public void setShowClose(boolean close) {
* ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
- * @see CTabFolder#setDirtyIndicatorCloseStyle(boolean)
+ * @see CTabFolder#setDirtyIndicatorStyle(boolean)
* @since 3.134
*/
public void setShowDirty(boolean dirty) {
diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java
index eaded3f5337..0f6115be3a7 100644
--- a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java
+++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet393.java
@@ -32,7 +32,7 @@ public static void main(String[] args) {
CTabFolder folder = new CTabFolder(shell, SWT.CLOSE | SWT.BORDER);
folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- folder.setDirtyIndicatorCloseStyle(true);
+ folder.setDirtyIndicatorStyle(true);
for (int i = 0; i < 4; i++) {
CTabItem item = new CTabItem(folder, SWT.NONE);
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 baec16653b3..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
@@ -90,10 +90,10 @@ public void test_setShowDirty() {
@Test
public void test_dirtyIndicatorCloseStyle() {
- assertFalse(cTabFolder.getDirtyIndicatorCloseStyle());
- cTabFolder.setDirtyIndicatorCloseStyle(true);
- assertTrue(cTabFolder.getDirtyIndicatorCloseStyle());
- cTabFolder.setDirtyIndicatorCloseStyle(false);
- assertFalse(cTabFolder.getDirtyIndicatorCloseStyle());
+ assertFalse(cTabFolder.getDirtyIndicatorStyle());
+ cTabFolder.setDirtyIndicatorStyle(true);
+ assertTrue(cTabFolder.getDirtyIndicatorStyle());
+ cTabFolder.setDirtyIndicatorStyle(false);
+ assertFalse(cTabFolder.getDirtyIndicatorStyle());
}
}