diff --git a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/TableOwnerDrawSupport.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/TableOwnerDrawSupport.java index 7f4e72a346df..a4b1ffd4257e 100644 --- a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/TableOwnerDrawSupport.java +++ b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/TableOwnerDrawSupport.java @@ -25,6 +25,8 @@ import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; +import org.eclipse.jface.viewers.ColumnViewerSelectionColorListener; + /** * Adds owner draw support for tables. @@ -83,7 +85,7 @@ public void handleEvent(Event event) { measureItem(event); break; case SWT.EraseItem: - event.detail &= ~SWT.FOREGROUND; + event.detail&= ~SWT.FOREGROUND; break; case SWT.PaintItem: performPaint(event); @@ -147,7 +149,9 @@ private void performPaint(Event event) { Color oldForeground= gc.getForeground(); Color oldBackground= gc.getBackground(); - if (!isSelected) { + if (isSelected) { + ColumnViewerSelectionColorListener.drawSelection(event); + } else { Color foreground= item.getForeground(index); gc.setForeground(foreground); diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerSelectionColorListener.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerSelectionColorListener.java new file mode 100644 index 000000000000..e09bc381fffa --- /dev/null +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewerSelectionColorListener.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2024 SAP SE. + * + * 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 + * + * Contributors: + * SAP SE - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.viewers; + +import java.util.Arrays; + +import org.eclipse.jface.resource.ColorRegistry; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Scrollable; + +/** + * EraseItem event listener that provides custom selection coloring for JFace + * viewers. This listener only activates when no custom owner draw label + * provider is registered, ensuring it doesn't conflict with existing custom + * drawing implementations. + *
+ * The listener provides different colors for: + *
+ * This method is idempotent - calling it multiple times on the same viewer has + * no additional effect. + *
+ * + * @param viewer the viewer to which the listener should be added + */ + public static void addListenerToViewer(StructuredViewer viewer) { + if ("gtk".equals(SWT.getPlatform())) { //$NON-NLS-1$ + return; // Skip on Linux + } + + Control control = viewer.getControl(); + if (control.isDisposed() || isListenerRegistered(control)) { + return; // Already registered or disposed + } + + ColumnViewerSelectionColorListener listener = new ColumnViewerSelectionColorListener(); + control.setData(LISTENER_KEY, listener); + control.addListener(SWT.EraseItem, listener); + } + + private static boolean isListenerRegistered(Control control) { + return control.getData(LISTENER_KEY) != null; + } + + @Override + public void handleEvent(Event event) { + if ((event.detail & SWT.SELECTED) == 0) { + return; // Not selected + } + + if (event.widget instanceof Control control && !control.isEnabled()) { + return; // Disabled control + } + + if (hasAdditionalEraseItemListeners(event)) { + return; // Let other listeners handle selection + } + + drawSelection(event); + } + + /** + * Checks if additional EraseItem listeners were registered after this listener + * that are NOT the OwnerDrawListener. This allows user code to override the + * selection coloring by adding their own EraseItem listener, while still + * allowing StyledCellLabelProvider to work (which uses OwnerDrawListener but + * doesn't draw selection). + * + * @param event the erase event + * @returntrue if other custom listeners are present that should
+ * handle selection, false otherwise
+ */
+ private boolean hasAdditionalEraseItemListeners(Event event) {
+ if (!(event.widget instanceof Control control)) {
+ return false;
+ }
+
+ Listener[] listeners = control.getListeners(SWT.EraseItem);
+ Object ownerDrawListener = control.getData(OWNER_DRAW_LISTENER_KEY);
+ return Arrays.stream(listeners).anyMatch(l -> l != this && l != ownerDrawListener);
+ }
+
+ /**
+ * Draws custom selection coloring for the given event.
+ * + * This method provides consistent selection rendering across different viewers + * and owner draw implementations. It handles both focused and unfocused + * selection states using themed colors from the ColorRegistry with appropriate + * fallbacks. + *
+ * + * @param event the erase event containing the widget, GC, and coordinates + * @since 3.32 + */ + public static void drawSelection(Event event) { + if ("gtk".equals(SWT.getPlatform())) { //$NON-NLS-1$ + return; // Skip on Linux + } + + Control control = (Control) event.widget; + GC gc = event.gc; + + Color backgroundColor; + Color foregroundColor; + + if (control.isFocusControl()) { + backgroundColor = getSelectionColor(COLOR_SELECTION_BG_FOCUS, event.display); + foregroundColor = getSelectionColor(COLOR_SELECTION_FG_FOCUS, event.display); + } else { + backgroundColor = getSelectionColor(COLOR_SELECTION_BG_NO_FOCUS, event.display); + foregroundColor = getSelectionColor(COLOR_SELECTION_FG_NO_FOCUS, event.display); + } + + gc.setBackground(backgroundColor); + gc.setForeground(foregroundColor); + + int width = event.width; + if (event.widget instanceof Scrollable scrollable) { + width = scrollable.getClientArea().width; + } + + gc.fillRectangle(0, event.y, width, event.height); + + // Remove SELECTED and BACKGROUND flags to prevent native drawing from + // overwriting our custom colors + event.detail &= ~(SWT.SELECTED | SWT.BACKGROUND); + } + + private static Color getSelectionColor(String key, org.eclipse.swt.graphics.Device device) { + ColorRegistry registry = JFaceResources.getColorRegistry(); + + if (registry.hasValueFor(key)) { + return registry.get(key); + } + + RGB systemColor; + switch (key) { + case COLOR_SELECTION_BG_FOCUS: + systemColor = device.getSystemColor(SWT.COLOR_TITLE_BACKGROUND).getRGB(); + break; + case COLOR_SELECTION_FG_FOCUS: + systemColor = device.getSystemColor(SWT.COLOR_WHITE).getRGB(); + break; + case COLOR_SELECTION_BG_NO_FOCUS: + systemColor = device.getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND).getRGB(); + break; + case COLOR_SELECTION_FG_NO_FOCUS: + systemColor = device.getSystemColor(SWT.COLOR_TITLE_INACTIVE_FOREGROUND).getRGB(); + break; + default: + systemColor = device.getSystemColor(SWT.COLOR_LIST_SELECTION).getRGB(); + break; + } + + registry.put(key, systemColor); + return registry.get(key); + } + +} diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/StructuredViewer.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/StructuredViewer.java index 5b1ab4432d31..08dc398839e3 100644 --- a/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/StructuredViewer.java +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/StructuredViewer.java @@ -1210,6 +1210,7 @@ public void widgetDefaultSelected(SelectionEvent e) { }); handler.addPostSelectionListener(widgetSelectedAdapter(this::handlePostSelect)); handler.addOpenListener(StructuredViewer.this::handleOpen); + ColumnViewerSelectionColorListener.addListenerToViewer(this); } /** diff --git a/bundles/org.eclipse.ui.themes/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.themes/META-INF/MANIFEST.MF index a1a0eafd8698..f9d231a3844e 100644 --- a/bundles/org.eclipse.ui.themes/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.ui.themes/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.ui.themes;singleton:=true -Bundle-Version: 1.2.2900.qualifier +Bundle-Version: 1.2.3000.qualifier Bundle-Vendor: %Plugin.providerName Bundle-Localization: plugin Require-Bundle: org.eclipse.e4.ui.css.swt.theme diff --git a/bundles/org.eclipse.ui.themes/css/dark/e4-dark_preferencestyle.css b/bundles/org.eclipse.ui.themes/css/dark/e4-dark_preferencestyle.css index 6e74b61901a1..8267a89fd83e 100644 --- a/bundles/org.eclipse.ui.themes/css/dark/e4-dark_preferencestyle.css +++ b/bundles/org.eclipse.ui.themes/css/dark/e4-dark_preferencestyle.css @@ -76,4 +76,6 @@ IEclipsePreferences#org-eclipse-ui-workbench:org-eclipse-ui-themes { /* pseudo a 'org.eclipse.ui.editors.rangeIndicatorColor=27,118,153' 'org.eclipse.jface.REVISION_NEWEST_COLOR=75,44,3' 'org.eclipse.jface.REVISION_OLDEST_COLOR=154,113,61' + 'org.eclipse.jface.SELECTION_FOREGROUND_NO_FOCUS=240,240,240' + 'org.eclipse.jface.SELECTION_BACKGROUND_NO_FOCUS=95,95,95' } diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/ColumnViewerSelectionColorFactory.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/ColumnViewerSelectionColorFactory.java new file mode 100644 index 000000000000..e45464be95b6 --- /dev/null +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/ColumnViewerSelectionColorFactory.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2024 SAP SE. + * + * 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 + * + * Contributors: + * SAP SE - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.internal.themes; + +import java.util.Hashtable; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.themes.IColorFactory; + +/** + * Color factory for viewer selection colors that adapts to the OS/desktop + * theme. Provides default colors based on system colors for focused and + * unfocused selections. + *+ * The default colors are based on system title bar colors which automatically + * adapt to light/dark themes and high contrast modes. Themes can override these + * defaults to provide custom styling. + *
+ * + * @since 3.39 + */ +public class ColumnViewerSelectionColorFactory implements IColorFactory, IExecutableExtension { + + private String color = null; + + @Override + public RGB createColor() { + Display display = Display.getDefault(); + + if ("SELECTED_CELL_BACKGROUND".equals(color)) { //$NON-NLS-1$ + return display.getSystemColor(SWT.COLOR_TITLE_BACKGROUND).getRGB(); + + } else if ("SELECTED_CELL_FOREGROUND".equals(color)) { //$NON-NLS-1$ + return display.getSystemColor(SWT.COLOR_WHITE).getRGB(); + + } else if ("SELECTED_CELL_BACKGROUND_NO_FOCUS".equals(color)) { //$NON-NLS-1$ + return display.getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND).getRGB(); + + } else if ("SELECTED_CELL_FOREGROUND_NO_FOCUS".equals(color)) { //$NON-NLS-1$ + return display.getSystemColor(SWT.COLOR_TITLE_INACTIVE_FOREGROUND).getRGB(); + + } else { + return new RGB(0, 0, 0); + } + } + + @Override + public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { + if (data instanceof Hashtable table) { + this.color = (String) table.get("color"); //$NON-NLS-1$ + } + } +} diff --git a/bundles/org.eclipse.ui/plugin.properties b/bundles/org.eclipse.ui/plugin.properties index 27e4672696c8..3fe7bac81114 100644 --- a/bundles/org.eclipse.ui/plugin.properties +++ b/bundles/org.eclipse.ui/plugin.properties @@ -371,6 +371,15 @@ Color.revisionNewestDesc=Background color for the newest revision shown in a tex Color.revisionOldest=Oldest revision background color 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. +Color.viewerSelectionBackgroundFocused=Viewer selection background (focused) +Color.viewerSelectionBackgroundFocusedDesc=Background color for selected items in viewers when the control has focus. Defaults to system highlight color. (No effect on Linux) +Color.viewerSelectionForegroundFocused=Viewer selection foreground (focused) +Color.viewerSelectionForegroundFocusedDesc=Foreground color for selected items in viewers when the control has focus. (No effect on Linux) +Color.viewerSelectionBackgroundNoFocus=Viewer selection background (no focus) +Color.viewerSelectionBackgroundNoFocusDesc=Background color for selected items in viewers when the control does not have focus. (No effect on Linux) +Color.viewerSelectionForegroundNoFocus=Viewer selection foreground (no focus) +Color.viewerSelectionForegroundNoFocusDesc=Foreground color for selected items in viewers when the control does not have focus. (No effect on Linux) + Color.showKeysForeground=Keys foreground color Color.showKeysForegroundDesc=Color for foreground of the control to visualize pressed keys Color.showKeysBackground=Keys background color diff --git a/bundles/org.eclipse.ui/plugin.xml b/bundles/org.eclipse.ui/plugin.xml index 127f5dc07ef7..ccddfd5116ed 100644 --- a/bundles/org.eclipse.ui/plugin.xml +++ b/bundles/org.eclipse.ui/plugin.xml @@ -1741,6 +1741,72 @@ + +