From 5639d0cedbd1e6ca6ba7608bbf1b8c0ace593987 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Sat, 30 Nov 2024 20:51:54 +0100 Subject: [PATCH 1/2] Change rendering of dirty indicator in tabs - using a circle Tabs render dirty parts by showing a `*` in front of the tab name (e.g., in front of the file name. This information is hard to see by developers. This change introduces a graphical indicator on the close button to highlight dirty (unsaved) changes. --- .../swt/custom/CTabFolderRenderer.java | 79 +++++++++++-------- .../org/eclipse/swt/custom/CTabItem.java | 30 +++++++ 2 files changed, 76 insertions(+), 33 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 9edf33fbf84..3206695cbfe 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 @@ -878,44 +878,57 @@ void drawBody(GC gc, Rectangle bounds, int state) { } } - void drawClose(GC gc, Rectangle closeRect, int closeImageState) { + void drawClose(GC gc, Rectangle closeRect, int closeImageState, boolean showDirtyIndicator) { if (closeRect.width == 0 || closeRect.height == 0) return; - // draw X with length of this constant - final int lineLength = 8; - int x = closeRect.x + Math.max(1, (closeRect.width-lineLength)/2); - int y = closeRect.y + Math.max(1, (closeRect.height-lineLength)/2); - y += parent.onBottom ? -1 : 1; int originalLineWidth = gc.getLineWidth(); - Color originalForeground = gc.getForeground(); - switch (closeImageState & (SWT.HOT | SWT.SELECTED | SWT.BACKGROUND)) { - case SWT.NONE: { - drawCloseLines(gc, x, y , lineLength, false); - break; - } - case SWT.HOT: { - drawCloseLines(gc, x, y , lineLength, true); - break; - } - case SWT.SELECTED: { - drawCloseLines(gc, x, y , lineLength, true); - break; - } - case SWT.BACKGROUND: { - int[] shape = new int[] {x,y, x+10,y, x+10,y+10, x,y+10}; - drawBackground(gc, shape, false); - break; - } - } - gc.setLineWidth(originalLineWidth); - gc.setForeground(originalForeground); + int state = closeImageState & (SWT.HOT | SWT.SELECTED | SWT.BACKGROUND); + if (state == SWT.NONE) { + if (showDirtyIndicator) + drawDirtyIndicator(gc, closeRect, false); + else + drawCloseButton(gc, closeRect, false); + } else if (state == SWT.HOT || state == SWT.SELECTED) { + drawCloseButton(gc, closeRect, true); + } else if (state == SWT.BACKGROUND) { + if (showDirtyIndicator) + drawDirtyIndicator(gc, closeRect, false); + else + drawBackground(gc, closeRect, SWT.BACKGROUND); + } + gc.setLineWidth(originalLineWidth); + } + + private void drawDirtyIndicator(GC gc, Rectangle closeRect, boolean hot) { + String DIRTY_INDICATOR = "●"; + + Point stringExtent = gc.stringExtent(DIRTY_INDICATOR); + int x = closeRect.x + (closeRect.width - stringExtent.x) / 2; + int y = closeRect.y + (closeRect.height - stringExtent.y) / 2; + gc.drawString(DIRTY_INDICATOR, x, y, true); + } + + private void drawCloseBackground(GC gc, Rectangle closeRect, Color backgroundColor) { + Color originalBackground = gc.getBackground(); + gc.setBackground(backgroundColor); + gc.setForeground(originalBackground); + gc.fillRoundRectangle(closeRect.x + 1, closeRect.y + 2, closeRect.width - 2, closeRect.height - 2, 4, 4); + gc.setBackground(originalBackground); } - private void drawCloseLines(GC gc, int x, int y, int lineLength, boolean hot) { + + private void drawCloseButton(GC gc, Rectangle closeRect, boolean hot) { if (hot) { - gc.setLineWidth(gc.getLineWidth() + 2); - gc.setForeground(getFillColor()); + drawCloseBackground(gc, closeRect, parent.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); +// gc.setLineWidth(gc.getLineWidth() + 2); + gc.setForeground(gc.getBackground()); } + // draw X with length of this constant + final int lineLength = 9; + int x = closeRect.x + Math.max(1, (closeRect.width-lineLength)/2); + int y = closeRect.y + Math.max(1, (closeRect.height-lineLength)/2); + y += parent.onBottom ? -1 : 1; + gc.setLineCap(SWT.CAP_ROUND); gc.drawLine(x, y, x + lineLength, y + lineLength); gc.drawLine(x, y + lineLength, x + lineLength, y); @@ -1464,7 +1477,7 @@ void drawSelected(int itemIndex, GC gc, Rectangle bounds, int state ) { gc.setBackground(orginalBackground); } } - if (shouldDrawCloseIcon(item)) drawClose(gc, item.closeRect, item.closeImageState); + if (shouldDrawCloseIcon(item)) drawClose(gc, item.closeRect, item.closeImageState, item.showDirty); } } @@ -1673,7 +1686,7 @@ void drawUnselected(int index, GC gc, Rectangle bounds, int state) { gc.setFont(gcFont); } // draw close - if (shouldDrawCloseIcon(item)) drawClose(gc, item.closeRect, item.closeImageState); + if (shouldDrawCloseIcon(item)) drawClose(gc, item.closeRect, item.closeImageState, item.showDirty); } } 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 884c44354db..4bc8154a851 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,20 @@ public boolean getShowClose() { checkWidget(); return showClose; } + +/** + * Returns true to indicate that the dirty indicator should be shown. + * Otherwise return false. + * + * @return true if the dirty indicatorn should be shown + * + * @since 3.129 + */ +public boolean getShowDirty() { + checkWidget(); + return showClose; +} + /** * Returns the receiver's tool tip text, or null if it has * not been set. @@ -490,6 +505,21 @@ public void setShowClose(boolean close) { showClose = close; parent.updateFolder(CTabFolder.REDRAW_TABS); } + +/** + * Sets to true to indicate that the dirty indicator should be shown. + * + * @param dirty the new value whether the dirty indicator shall be shown + * + * @since 3.129 + */ +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. From 8f9835592ac738b47ed51eff8cebc9d2dce467b9 Mon Sep 17 00:00:00 2001 From: Michael Schneider Date: Fri, 31 Jan 2025 17:38:16 +0100 Subject: [PATCH 2/2] Rename method to `isDirtyIndicatorShown()` --- .../common/org/eclipse/swt/custom/CTabItem.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 4bc8154a851..79355c87cdd 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 @@ -282,13 +282,13 @@ public boolean getShowClose() { * Returns true to indicate that the dirty indicator should be shown. * Otherwise return false. * - * @return true if the dirty indicatorn should be shown + * @return true if the dirty indicator should be shown * * @since 3.129 */ -public boolean getShowDirty() { +public boolean isDirtyIndicatorShown() { checkWidget(); - return showClose; + return showDirty; } /**