-
Notifications
You must be signed in to change notification settings - Fork 248
Proper selection coloring in JFace Structured Viewers #3659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
314a045
c2654d1
abe68b1
339d07c
27abb7c
9b24f71
d688d34
bc866ce
095c986
ada2dc8
b921c1a
1ca6ba6
b268373
81f9cd0
0838f15
172eb01
28563b5
521271e
ef799cc
632a201
0ec8982
d409d11
07c2a89
a9b7ffa
4a8b5fc
cc2b740
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
| * <p> | ||
|
fedejeanne marked this conversation as resolved.
|
||
| * The listener provides different colors for: | ||
| * <ul> | ||
| * <li>Selected items when the control has focus</li> | ||
| * <li>Selected items when the control doesn't have focus</li> | ||
| * </ul> | ||
| * </p> | ||
| * | ||
| * @see org.eclipse.jface.viewers.OwnerDrawLabelProvider | ||
| * @see org.eclipse.jface.viewers.StyledCellLabelProvider | ||
| * @see org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter | ||
| * @since 3.39 | ||
| */ | ||
| public class ColumnViewerSelectionColorListener implements Listener { | ||
|
Christopher-Hermann marked this conversation as resolved.
|
||
|
|
||
| private static final String LISTENER_KEY = "org.eclipse.jface.viewers.selection_color_listener"; //$NON-NLS-1$ | ||
| private static final String OWNER_DRAW_LISTENER_KEY = "owner_draw_label_provider_listener"; //$NON-NLS-1$ | ||
|
|
||
| private static final String COLOR_SELECTION_BG_FOCUS = "org.eclipse.jface.SELECTION_BACKGROUND_FOCUSED"; //$NON-NLS-1$ | ||
| private static final String COLOR_SELECTION_FG_FOCUS = "org.eclipse.jface.SELECTION_FOREGROUND_FOCUSED"; //$NON-NLS-1$ | ||
| private static final String COLOR_SELECTION_BG_NO_FOCUS = "org.eclipse.jface.SELECTION_BACKGROUND_NO_FOCUS"; //$NON-NLS-1$ | ||
| private static final String COLOR_SELECTION_FG_NO_FOCUS = "org.eclipse.jface.SELECTION_FOREGROUND_NO_FOCUS"; //$NON-NLS-1$ | ||
|
|
||
| /** | ||
| * Registers the selection color listener on the given viewer. | ||
| * <p> | ||
| * This method is idempotent - calling it multiple times on the same viewer has | ||
| * no additional effect. | ||
| * </p> | ||
| * | ||
| * @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 | ||
| * @return <code>true</code> if other custom listeners are present that should | ||
| * handle selection, <code>false</code> 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); | ||
| } | ||
|
fedejeanne marked this conversation as resolved.
|
||
|
|
||
| /** | ||
| * Draws custom selection coloring for the given event. | ||
| * <p> | ||
| * 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. | ||
| * </p> | ||
| * | ||
| * @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); | ||
| } | ||
|
fedejeanne marked this conversation as resolved.
Outdated
|
||
|
|
||
| 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); | ||
|
fedejeanne marked this conversation as resolved.
Outdated
|
||
| } | ||
|
fedejeanne marked this conversation as resolved.
|
||
|
|
||
| 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); | ||
| } | ||
|
fedejeanne marked this conversation as resolved.
Outdated
|
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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. | ||||||||||||
| * <p> | ||||||||||||
| * 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. | ||||||||||||
| * </p> | ||||||||||||
| * | ||||||||||||
| * @since 3.39 | ||||||||||||
| */ | ||||||||||||
| public class ColumnViewerSelectionColorFactory implements IColorFactory, IExecutableExtension { | ||||||||||||
|
|
||||||||||||
| private String color = null; | ||||||||||||
|
|
||||||||||||
| @Override | ||||||||||||
| public RGB createColor() { | ||||||||||||
| Display display = Display.getDefault(); | ||||||||||||
|
||||||||||||
| Display display = Display.getDefault(); | |
| Display display = Display.getCurrent(); | |
| if (display == null) { | |
| display = Display.getDefault(); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be fine with using getCurrent() too. WDYT @Christopher-Hermann ?
Uh oh!
There was an error while loading. Please reload this page.