Skip to content

Commit 5bc6f96

Browse files
vogellaclaude
andcommitted
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. Also fix: clicking the dirty indicator no longer closes the tab unless close is independently enabled (via SWT.CLOSE or setShowClose(true)). Previously, enabling dirtyIndicatorStyle would implicitly make dirty tabs closeable. The dirty indicator is now purely visual. - Guard onMouse MouseDown/MouseUp close handling with showClose check - Update _getToolTip to only show "Close" when close is enabled - Add Snippet394 to Snippets.md - Add tests for dirty indicator close behavior in CTabFolder tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 792dbd3 commit 5bc6f96

6 files changed

Lines changed: 143 additions & 24 deletions

File tree

bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1896,7 +1896,7 @@ public void run() {
18961896
}
18971897
}
18981898
if (item != null) {
1899-
if (item.closeRect.contains(x,y)){
1899+
if ((showClose || item.showClose) && item.closeRect.contains(x,y)){
19001900
item.closeImageState = SWT.SELECTED;
19011901
redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
19021902
update();
@@ -1977,7 +1977,7 @@ public void run() {
19771977
}
19781978
}
19791979
if (item != null) {
1980-
if (item.closeRect.contains(x,y)) {
1980+
if ((showClose || item.showClose) && item.closeRect.contains(x,y)) {
19811981
boolean selected = item.closeImageState == SWT.SELECTED;
19821982
item.closeImageState = SWT.HOT;
19831983
redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
@@ -3671,17 +3671,22 @@ public void setUnselectedCloseVisible(boolean visible) {
36713671
updateFolder(REDRAW);
36723672
}
36733673
/**
3674-
* Sets whether the dirty indicator uses the close button style. When enabled,
3674+
* Sets whether the dirty indicator style is enabled. When enabled,
36753675
* dirty items (marked via {@link CTabItem#setShowDirty(boolean)}) show a
36763676
* bullet dot at the close button location instead of the traditional
3677-
* <code>*</code> prefix. The bullet transforms into the close button on hover.
3677+
* <code>*</code> prefix. The bullet transforms into the close button on hover
3678+
* when close is enabled for the folder or the individual item.
3679+
* <p>
3680+
* Note: the dirty indicator is purely visual. Clicking the bullet does not
3681+
* close the tab unless close is independently enabled (via the
3682+
* {@link SWT#CLOSE} style or {@link CTabItem#setShowClose(boolean)}).
3683+
* </p>
36783684
* <p>
36793685
* The default value is <code>false</code> (traditional <code>*</code> prefix
36803686
* behavior).
36813687
* </p>
36823688
*
3683-
* @param useCloseButtonStyle <code>true</code> to use the bullet-on-close-button
3684-
* style for dirty indicators
3689+
* @param enabled <code>true</code> to enable the dirty indicator style
36853690
*
36863691
* @exception SWTException <ul>
36873692
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
@@ -3691,26 +3696,26 @@ public void setUnselectedCloseVisible(boolean visible) {
36913696
* @see CTabItem#setShowDirty(boolean)
36923697
* @since 3.134
36933698
*/
3694-
public void setDirtyIndicatorCloseStyle(boolean useCloseButtonStyle) {
3699+
public void setDirtyIndicatorStyle(boolean enabled) {
36953700
checkWidget();
3696-
if (dirtyIndicatorStyle == useCloseButtonStyle) return;
3697-
dirtyIndicatorStyle = useCloseButtonStyle;
3701+
if (dirtyIndicatorStyle == enabled) return;
3702+
dirtyIndicatorStyle = enabled;
36983703
updateFolder(REDRAW_TABS);
36993704
}
37003705
/**
3701-
* Returns whether the dirty indicator uses the close button style.
3706+
* Returns whether the dirty indicator style is enabled.
37023707
*
3703-
* @return <code>true</code> if the dirty indicator uses the close button style
3708+
* @return <code>true</code> if the dirty indicator style is enabled
37043709
*
37053710
* @exception SWTException <ul>
37063711
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
37073712
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
37083713
* </ul>
37093714
*
3710-
* @see #setDirtyIndicatorCloseStyle(boolean)
3715+
* @see #setDirtyIndicatorStyle(boolean)
37113716
* @since 3.134
37123717
*/
3713-
public boolean getDirtyIndicatorCloseStyle() {
3718+
public boolean getDirtyIndicatorStyle() {
37143719
checkWidget();
37153720
return dirtyIndicatorStyle;
37163721
}
@@ -4048,8 +4053,10 @@ String _getToolTip(int x, int y) {
40484053
CTabItem item = getItem(new Point (x, y));
40494054
if (item == null) return null;
40504055
if (!item.showing) return null;
4051-
if ((showClose || item.showClose || (dirtyIndicatorStyle && item.showDirty)) && item.closeRect.contains(x, y)) {
4052-
return SWT.getMessage("SWT_Close"); //$NON-NLS-1$
4056+
if (item.closeRect.contains(x, y)) {
4057+
if (showClose || item.showClose) {
4058+
return SWT.getMessage("SWT_Close"); //$NON-NLS-1$
4059+
}
40534060
}
40544061
return item.getToolTipText();
40554062
}

bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabItem.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ public boolean getShowClose() {
290290
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
291291
* </ul>
292292
*
293-
* @see CTabFolder#setDirtyIndicatorCloseStyle(boolean)
293+
* @see CTabFolder#setDirtyIndicatorStyle(boolean)
294294
* @since 3.134
295295
*/
296296
public boolean getShowDirty() {
@@ -514,7 +514,7 @@ public void setShowClose(boolean close) {
514514
/**
515515
* Marks this item as dirty (having unsaved changes). When the parent
516516
* folder's dirty indicator style is enabled via
517-
* {@link CTabFolder#setDirtyIndicatorCloseStyle(boolean)}, dirty items
517+
* {@link CTabFolder#setDirtyIndicatorStyle(boolean)}, dirty items
518518
* show a bullet dot at the close button location. The bullet transforms
519519
* into the close button on hover.
520520
*
@@ -525,7 +525,7 @@ public void setShowClose(boolean close) {
525525
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
526526
* </ul>
527527
*
528-
* @see CTabFolder#setDirtyIndicatorCloseStyle(boolean)
528+
* @see CTabFolder#setDirtyIndicatorStyle(boolean)
529529
* @since 3.134
530530
*/
531531
public void setShowDirty(boolean dirty) {

examples/org.eclipse.swt.snippets/Snippets.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ To contribute a new snippet, [create a snippet contribution as a pull request](h
118118
- [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")
119119
- [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")
120120
- [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")
121+
- [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)
121122

122123
### **Cursor**
123124
- [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")

examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet394.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static void main(String[] args) {
3232

3333
CTabFolder folder = new CTabFolder(shell, SWT.CLOSE | SWT.BORDER);
3434
folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
35-
folder.setDirtyIndicatorCloseStyle(true);
35+
folder.setDirtyIndicatorStyle(true);
3636

3737
for (int i = 0; i < 4; i++) {
3838
CTabItem item = new CTabItem(folder, SWT.NONE);

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabFolder.java

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,4 +939,115 @@ public void test_getItem_byPoint() {
939939
assertNull(notFound);
940940
}
941941

942+
@Test
943+
public void test_dirtyIndicator_doesNotCloseWithoutCloseEnabled() {
944+
// Create folder WITHOUT SWT.CLOSE style
945+
makeCleanEnvironment(SWT.NONE);
946+
shell.setLayout(new FillLayout());
947+
948+
for (int i = 0; i < 3; i++) {
949+
CTabItem item = new CTabItem(ctabFolder, SWT.NONE);
950+
item.setText("Tab " + i);
951+
Label content = new Label(ctabFolder, SWT.NONE);
952+
content.setText("Content " + i);
953+
item.setControl(content);
954+
}
955+
956+
ctabFolder.setDirtyIndicatorStyle(true);
957+
ctabFolder.getItem(0).setShowDirty(true);
958+
ctabFolder.setSelection(0);
959+
shell.setSize(800, 400);
960+
SwtTestUtil.openShell(shell);
961+
processEvents();
962+
963+
// The dirty item should still have a closeRect allocated for the bullet
964+
CTabItem dirtyItem = ctabFolder.getItem(0);
965+
Rectangle closeRect = dirtyItem.getBounds(); // just verify item exists
966+
967+
int itemCountBefore = ctabFolder.getItemCount();
968+
assertEquals(3, itemCountBefore);
969+
970+
// Simulate mouse down + up on the closeRect area via events
971+
// The item should NOT be disposed because close is not enabled
972+
Rectangle itemBounds = dirtyItem.getBounds();
973+
Event mouseDown = new Event();
974+
mouseDown.type = SWT.MouseDown;
975+
mouseDown.button = 1;
976+
mouseDown.x = itemBounds.x + itemBounds.width - 5;
977+
mouseDown.y = itemBounds.y + itemBounds.height / 2;
978+
ctabFolder.notifyListeners(SWT.MouseDown, mouseDown);
979+
processEvents();
980+
981+
Event mouseUp = new Event();
982+
mouseUp.type = SWT.MouseUp;
983+
mouseUp.button = 1;
984+
mouseUp.x = mouseDown.x;
985+
mouseUp.y = mouseDown.y;
986+
ctabFolder.notifyListeners(SWT.MouseUp, mouseUp);
987+
processEvents();
988+
989+
// Item should still exist - dirty indicator click should not close the tab
990+
assertFalse(dirtyItem.isDisposed(), "Dirty item should not be disposed when close is not enabled");
991+
assertEquals(3, ctabFolder.getItemCount(), "Item count should remain unchanged");
992+
}
993+
994+
@Test
995+
public void test_dirtyIndicator_closesWhenCloseEnabled() {
996+
// Create folder WITH SWT.CLOSE style
997+
makeCleanEnvironment(SWT.CLOSE);
998+
shell.setLayout(new FillLayout());
999+
1000+
for (int i = 0; i < 3; i++) {
1001+
CTabItem item = new CTabItem(ctabFolder, SWT.NONE);
1002+
item.setText("Tab " + i);
1003+
Label content = new Label(ctabFolder, SWT.NONE);
1004+
content.setText("Content " + i);
1005+
item.setControl(content);
1006+
}
1007+
1008+
ctabFolder.setDirtyIndicatorStyle(true);
1009+
ctabFolder.getItem(0).setShowDirty(true);
1010+
ctabFolder.setSelection(0);
1011+
shell.setSize(800, 400);
1012+
SwtTestUtil.openShell(shell);
1013+
processEvents();
1014+
1015+
CTabItem dirtyItem = ctabFolder.getItem(0);
1016+
1017+
// Access closeRect via reflection to click precisely on it
1018+
try {
1019+
Field closeRectField = CTabItem.class.getDeclaredField("closeRect");
1020+
closeRectField.setAccessible(true);
1021+
Rectangle closeRectValue = (Rectangle) closeRectField.get(dirtyItem);
1022+
1023+
// closeRect should be allocated (non-zero) for dirty items
1024+
assertTrue(closeRectValue.width > 0 && closeRectValue.height > 0,
1025+
"closeRect should be allocated for dirty item");
1026+
1027+
// Simulate mouse down on the close rect
1028+
Event mouseDown = new Event();
1029+
mouseDown.type = SWT.MouseDown;
1030+
mouseDown.button = 1;
1031+
mouseDown.x = closeRectValue.x + closeRectValue.width / 2;
1032+
mouseDown.y = closeRectValue.y + closeRectValue.height / 2;
1033+
ctabFolder.notifyListeners(SWT.MouseDown, mouseDown);
1034+
processEvents();
1035+
1036+
// Simulate mouse up on the same location
1037+
Event mouseUp = new Event();
1038+
mouseUp.type = SWT.MouseUp;
1039+
mouseUp.button = 1;
1040+
mouseUp.x = mouseDown.x;
1041+
mouseUp.y = mouseDown.y;
1042+
ctabFolder.notifyListeners(SWT.MouseUp, mouseUp);
1043+
processEvents();
1044+
1045+
// Item should be disposed - close is enabled via SWT.CLOSE
1046+
assertTrue(dirtyItem.isDisposed(), "Dirty item should be disposed when close is enabled");
1047+
assertEquals(2, ctabFolder.getItemCount(), "Item count should decrease by 1");
1048+
} catch (NoSuchFieldException | IllegalAccessException e) {
1049+
fail("Failed to access closeRect via reflection: " + e.getMessage());
1050+
}
1051+
}
1052+
9421053
}

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabItem.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,10 @@ public void test_setShowDirty() {
9090

9191
@Test
9292
public void test_dirtyIndicatorCloseStyle() {
93-
assertFalse(cTabFolder.getDirtyIndicatorCloseStyle());
94-
cTabFolder.setDirtyIndicatorCloseStyle(true);
95-
assertTrue(cTabFolder.getDirtyIndicatorCloseStyle());
96-
cTabFolder.setDirtyIndicatorCloseStyle(false);
97-
assertFalse(cTabFolder.getDirtyIndicatorCloseStyle());
93+
assertFalse(cTabFolder.getDirtyIndicatorStyle());
94+
cTabFolder.setDirtyIndicatorStyle(true);
95+
assertTrue(cTabFolder.getDirtyIndicatorStyle());
96+
cTabFolder.setDirtyIndicatorStyle(false);
97+
assertFalse(cTabFolder.getDirtyIndicatorStyle());
9898
}
9999
}

0 commit comments

Comments
 (0)