Skip to content

Commit 29bf05a

Browse files
Handle viewer selection color
1 parent 322b475 commit 29bf05a

File tree

7 files changed

+350
-2
lines changed

7 files changed

+350
-2
lines changed

bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/TableOwnerDrawSupport.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.eclipse.swt.widgets.Table;
2626
import org.eclipse.swt.widgets.TableItem;
2727

28+
import org.eclipse.jface.viewers.ColumnViewerSelectionColorListener;
29+
2830

2931
/**
3032
* Adds owner draw support for tables.
@@ -83,7 +85,7 @@ public void handleEvent(Event event) {
8385
measureItem(event);
8486
break;
8587
case SWT.EraseItem:
86-
event.detail &= ~SWT.FOREGROUND;
88+
event.detail&= ~SWT.FOREGROUND;
8789
break;
8890
case SWT.PaintItem:
8991
performPaint(event);
@@ -147,7 +149,9 @@ private void performPaint(Event event) {
147149
Color oldForeground= gc.getForeground();
148150
Color oldBackground= gc.getBackground();
149151

150-
if (!isSelected) {
152+
if (isSelected) {
153+
ColumnViewerSelectionColorListener.drawSelection(event);
154+
} else {
151155
Color foreground= item.getForeground(index);
152156
gc.setForeground(foreground);
153157

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 SAP SE.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* SAP SE - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.jface.viewers;
15+
16+
import java.util.Arrays;
17+
18+
import org.eclipse.jface.resource.ColorRegistry;
19+
import org.eclipse.jface.resource.JFaceResources;
20+
import org.eclipse.swt.SWT;
21+
import org.eclipse.swt.graphics.Color;
22+
import org.eclipse.swt.graphics.GC;
23+
import org.eclipse.swt.graphics.RGB;
24+
import org.eclipse.swt.widgets.Control;
25+
import org.eclipse.swt.widgets.Event;
26+
import org.eclipse.swt.widgets.Listener;
27+
import org.eclipse.swt.widgets.Scrollable;
28+
29+
/**
30+
* EraseItem event listener that provides custom selection coloring for JFace
31+
* viewers. This listener only activates when no custom owner draw label
32+
* provider is registered, ensuring it doesn't conflict with existing custom
33+
* drawing implementations.
34+
* <p>
35+
* The listener provides different colors for:
36+
* <ul>
37+
* <li>Selected items when the control has focus</li>
38+
* <li>Selected items when the control doesn't have focus</li>
39+
* </ul>
40+
* </p>
41+
*
42+
* @see org.eclipse.jface.viewers.OwnerDrawLabelProvider
43+
* @see org.eclipse.jface.viewers.StyledCellLabelProvider
44+
* @see org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter
45+
* @since 3.39
46+
*/
47+
public class ColumnViewerSelectionColorListener implements Listener {
48+
49+
private static final String LISTENER_KEY = "org.eclipse.jface.viewers.selection_color_listener"; //$NON-NLS-1$
50+
private static final String OWNER_DRAW_LISTENER_KEY = "owner_draw_label_provider_listener"; //$NON-NLS-1$
51+
52+
private static final String COLOR_SELECTION_BG_FOCUS = "org.eclipse.jface.SELECTION_BACKGROUND_FOCUSED"; //$NON-NLS-1$
53+
private static final String COLOR_SELECTION_FG_FOCUS = "org.eclipse.jface.SELECTION_FOREGROUND_FOCUSED"; //$NON-NLS-1$
54+
private static final String COLOR_SELECTION_BG_NO_FOCUS = "org.eclipse.jface.SELECTION_BACKGROUND_NO_FOCUS"; //$NON-NLS-1$
55+
private static final String COLOR_SELECTION_FG_NO_FOCUS = "org.eclipse.jface.SELECTION_FOREGROUND_NO_FOCUS"; //$NON-NLS-1$
56+
57+
/**
58+
* Registers the selection color listener on the given viewer.
59+
* <p>
60+
* This method is idempotent - calling it multiple times on the same viewer has
61+
* no additional effect.
62+
* </p>
63+
*
64+
* @param viewer the viewer to which the listener should be added
65+
*/
66+
public static void addListenerToViewer(StructuredViewer viewer) {
67+
if ("gtk".equals(SWT.getPlatform())) { //$NON-NLS-1$
68+
return; // Skip on Linux
69+
}
70+
71+
Control control = viewer.getControl();
72+
if (control.isDisposed() || isListenerRegistered(control)) {
73+
return; // Already registered or disposed
74+
}
75+
76+
ColumnViewerSelectionColorListener listener = new ColumnViewerSelectionColorListener();
77+
control.setData(LISTENER_KEY, listener);
78+
control.addListener(SWT.EraseItem, listener);
79+
}
80+
81+
private static boolean isListenerRegistered(Control control) {
82+
return control.getData(LISTENER_KEY) != null;
83+
}
84+
85+
@Override
86+
public void handleEvent(Event event) {
87+
if ((event.detail & SWT.SELECTED) == 0) {
88+
return; // Not selected
89+
}
90+
91+
if (event.widget instanceof Control control && !control.isEnabled()) {
92+
return; // Disabled control
93+
}
94+
95+
if (hasAdditionalEraseItemListeners(event)) {
96+
return; // Let other listeners handle selection
97+
}
98+
99+
drawSelection(event);
100+
}
101+
102+
/**
103+
* Checks if additional EraseItem listeners were registered after this listener
104+
* that are NOT the OwnerDrawListener. This allows user code to override the
105+
* selection coloring by adding their own EraseItem listener, while still
106+
* allowing StyledCellLabelProvider to work (which uses OwnerDrawListener but
107+
* doesn't draw selection).
108+
*
109+
* @param event the erase event
110+
* @return <code>true</code> if other custom listeners are present that should
111+
* handle selection, <code>false</code> otherwise
112+
*/
113+
private boolean hasAdditionalEraseItemListeners(Event event) {
114+
if (!(event.widget instanceof Control control)) {
115+
return false;
116+
}
117+
118+
Listener[] listeners = control.getListeners(SWT.EraseItem);
119+
Object ownerDrawListener = control.getData(OWNER_DRAW_LISTENER_KEY);
120+
return Arrays.stream(listeners).anyMatch(l -> l != this && l != ownerDrawListener);
121+
}
122+
123+
/**
124+
* Draws custom selection coloring for the given event.
125+
* <p>
126+
* This method provides consistent selection rendering across different viewers
127+
* and owner draw implementations. It handles both focused and unfocused
128+
* selection states using themed colors from the ColorRegistry with appropriate
129+
* fallbacks.
130+
* </p>
131+
*
132+
* @param event the erase event containing the widget, GC, and coordinates
133+
* @since 3.32
134+
*/
135+
public static void drawSelection(Event event) {
136+
if ("gtk".equals(SWT.getPlatform())) { //$NON-NLS-1$
137+
return; // Skip on Linux
138+
}
139+
140+
Control control = (Control) event.widget;
141+
GC gc = event.gc;
142+
143+
Color backgroundColor;
144+
Color foregroundColor;
145+
146+
if (control.isFocusControl()) {
147+
backgroundColor = getSelectionColor(COLOR_SELECTION_BG_FOCUS, event.display);
148+
foregroundColor = getSelectionColor(COLOR_SELECTION_FG_FOCUS, event.display);
149+
} else {
150+
backgroundColor = getSelectionColor(COLOR_SELECTION_BG_NO_FOCUS, event.display);
151+
foregroundColor = getSelectionColor(COLOR_SELECTION_FG_NO_FOCUS, event.display);
152+
}
153+
154+
gc.setBackground(backgroundColor);
155+
gc.setForeground(foregroundColor);
156+
157+
int width = event.width;
158+
if (event.widget instanceof Scrollable scrollable) {
159+
width = scrollable.getClientArea().width;
160+
}
161+
162+
gc.fillRectangle(0, event.y, width, event.height);
163+
164+
// Remove SELECTED and BACKGROUND flags to prevent native drawing from
165+
// overwriting our custom colors
166+
event.detail &= ~(SWT.SELECTED | SWT.BACKGROUND);
167+
}
168+
169+
private static Color getSelectionColor(String key, org.eclipse.swt.graphics.Device device) {
170+
ColorRegistry registry = JFaceResources.getColorRegistry();
171+
172+
if (registry.hasValueFor(key)) {
173+
return registry.get(key);
174+
}
175+
176+
RGB systemColor;
177+
switch (key) {
178+
case COLOR_SELECTION_BG_FOCUS:
179+
systemColor = device.getSystemColor(SWT.COLOR_TITLE_BACKGROUND).getRGB();
180+
break;
181+
case COLOR_SELECTION_FG_FOCUS:
182+
systemColor = device.getSystemColor(SWT.COLOR_WHITE).getRGB();
183+
break;
184+
case COLOR_SELECTION_BG_NO_FOCUS:
185+
systemColor = device.getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND).getRGB();
186+
break;
187+
case COLOR_SELECTION_FG_NO_FOCUS:
188+
systemColor = device.getSystemColor(SWT.COLOR_TITLE_INACTIVE_FOREGROUND).getRGB();
189+
break;
190+
default:
191+
systemColor = device.getSystemColor(SWT.COLOR_LIST_SELECTION).getRGB();
192+
break;
193+
}
194+
195+
registry.put(key, systemColor);
196+
return registry.get(key);
197+
}
198+
199+
}

bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/StructuredViewer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,7 @@ public void widgetDefaultSelected(SelectionEvent e) {
12101210
});
12111211
handler.addPostSelectionListener(widgetSelectedAdapter(this::handlePostSelect));
12121212
handler.addOpenListener(StructuredViewer.this::handleOpen);
1213+
ColumnViewerSelectionColorListener.addListenerToViewer(this);
12131214
}
12141215

12151216
/**

bundles/org.eclipse.ui.themes/css/dark/e4-dark_preferencestyle.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,6 @@ IEclipsePreferences#org-eclipse-ui-workbench:org-eclipse-ui-themes { /* pseudo a
7676
'org.eclipse.ui.editors.rangeIndicatorColor=27,118,153'
7777
'org.eclipse.jface.REVISION_NEWEST_COLOR=75,44,3'
7878
'org.eclipse.jface.REVISION_OLDEST_COLOR=154,113,61'
79+
'org.eclipse.jface.SELECTION_FOREGROUND_NO_FOCUS=240,240,240'
80+
'org.eclipse.jface.SELECTION_BACKGROUND_NO_FOCUS=95,95,95'
7981
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 SAP SE.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* SAP SE - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.ui.internal.themes;
15+
16+
import java.util.Hashtable;
17+
import org.eclipse.core.runtime.IConfigurationElement;
18+
import org.eclipse.core.runtime.IExecutableExtension;
19+
import org.eclipse.swt.SWT;
20+
import org.eclipse.swt.graphics.RGB;
21+
import org.eclipse.swt.widgets.Display;
22+
import org.eclipse.ui.themes.IColorFactory;
23+
24+
/**
25+
* Color factory for viewer selection colors that adapts to the OS/desktop
26+
* theme. Provides default colors based on system colors for focused and
27+
* unfocused selections.
28+
* <p>
29+
* The default colors are based on system title bar colors which automatically
30+
* adapt to light/dark themes and high contrast modes. Themes can override these
31+
* defaults to provide custom styling.
32+
* </p>
33+
*
34+
* @since 3.39
35+
*/
36+
public class ColumnViewerSelectionColorFactory implements IColorFactory, IExecutableExtension {
37+
38+
private String color = null;
39+
40+
@Override
41+
public RGB createColor() {
42+
Display display = Display.getDefault();
43+
44+
if ("SELECTED_CELL_BACKGROUND".equals(color)) { //$NON-NLS-1$
45+
return display.getSystemColor(SWT.COLOR_TITLE_BACKGROUND).getRGB();
46+
47+
} else if ("SELECTED_CELL_FOREGROUND".equals(color)) { //$NON-NLS-1$
48+
return display.getSystemColor(SWT.COLOR_WHITE).getRGB();
49+
50+
} else if ("SELECTED_CELL_BACKGROUND_NO_FOCUS".equals(color)) { //$NON-NLS-1$
51+
return display.getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND).getRGB();
52+
53+
} else if ("SELECTED_CELL_FOREGROUND_NO_FOCUS".equals(color)) { //$NON-NLS-1$
54+
return display.getSystemColor(SWT.COLOR_TITLE_INACTIVE_FOREGROUND).getRGB();
55+
56+
} else {
57+
return new RGB(0, 0, 0);
58+
}
59+
}
60+
61+
@Override
62+
public void setInitializationData(IConfigurationElement config, String propertyName, Object data) {
63+
if (data instanceof Hashtable table) {
64+
this.color = (String) table.get("color"); //$NON-NLS-1$
65+
}
66+
}
67+
}

bundles/org.eclipse.ui/plugin.properties

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,15 @@ Color.revisionNewestDesc=Background color for the newest revision shown in a tex
371371
Color.revisionOldest=Oldest revision background color
372372
Color.revisionOldestDesc=Background color for the oldest revision shown in a text editor's ruler. Together with the newest revision background color this defines a gradient used for all revision from newest to oldest.
373373

374+
Color.viewerSelectionBackgroundFocused=Viewer selection background (focused)
375+
Color.viewerSelectionBackgroundFocusedDesc=Background color for selected items in viewers when the control has focus. Defaults to system highlight color. (No effect on Linux)
376+
Color.viewerSelectionForegroundFocused=Viewer selection foreground (focused)
377+
Color.viewerSelectionForegroundFocusedDesc=Foreground color for selected items in viewers when the control has focus. (No effect on Linux)
378+
Color.viewerSelectionBackgroundNoFocus=Viewer selection background (no focus)
379+
Color.viewerSelectionBackgroundNoFocusDesc=Background color for selected items in viewers when the control does not have focus. (No effect on Linux)
380+
Color.viewerSelectionForegroundNoFocus=Viewer selection foreground (no focus)
381+
Color.viewerSelectionForegroundNoFocusDesc=Foreground color for selected items in viewers when the control does not have focus. (No effect on Linux)
382+
374383
Color.showKeysForeground=Keys foreground color
375384
Color.showKeysForegroundDesc=Color for foreground of the control to visualize pressed keys
376385
Color.showKeysBackground=Keys background color

0 commit comments

Comments
 (0)