From 03e2ff3995e2e2ddebf19dcfeb568beb1cd85f8d Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Tue, 6 Oct 2026 23:51:49 +0200 Subject: [PATCH] Resolve CSS color and font definitions without the workbench MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IColorAndFontProvider used by the CSS engine was a DS service in org.eclipse.ui.workbench that read the current workbench theme. In e4 applications that have org.eclipse.ui.workbench on the runtime but no Workbench, for example through the E4 spies, every CSS value referencing a color or font definition failed with a NullPointerException. Without that bundle, no provider existed at all. The CSS engine now falls back to the JFace color and font registries, which the workbench keeps in sync with its current theme, so the workbench service is no longer needed. The workbench updates the JFace registries before it notifies theme listeners, so they see the new theme's values. A color, font or gradient definition that cannot be resolved is treated like unset, so the widget keeps its own color and font instead of turning black, switching to the default font or failing to paint. Assisted-by: multiple AI agents and layers of automated tooling 🤖 --- .../ui/css/swt/helpers/CSSSWTColorHelper.java | 11 +- .../ui/css/swt/helpers/CSSSWTFontHelper.java | 38 +++++++ .../CSSValueSWTColorConverterImpl.java | 6 +- .../css2/CSSPropertyBackgroundSWTHandler.java | 21 +++- .../css2/CSSPropertyFontSWTHandler.java | 14 +-- .../CSSPropertye4SelectedTabFillHandler.java | 16 +-- .../ui/internal/css/swt/ColorAndFontUtil.java | 38 +++---- .../css/swt/JFaceColorAndFontProvider.java} | 28 +++-- .../META-INF/MANIFEST.MF | 3 +- .../themes/WorkbenchThemeManager.java | 7 +- .../css/swt/helpers/CSSSWTHelperTestCase.java | 15 ++- .../e4/ui/tests/css/swt/CTabItemTest.java | 33 ++++++ .../ui/tests/css/swt/ColorDefinitionTest.java | 80 ++++++++++++-- .../ui/tests/css/swt/FontDefinitionTest.java | 102 ++++++++++++++++-- .../e4/ui/tests/css/swt/GradientTest.java | 62 +++++++++++ .../ui/tests/themes/JFaceThemeTest.java | 77 ++++++++++++- 16 files changed, 474 insertions(+), 77 deletions(-) rename bundles/{org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/ColorAndFontProviderImpl.java => org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/internal/css/swt/JFaceColorAndFontProvider.java} (53%) diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTColorHelper.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTColorHelper.java index bafb10abdd9..859589df519 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTColorHelper.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTColorHelper.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2015 Angelo Zerr and others. + * Copyright (c) 2008, 2026 Angelo Zerr and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -96,6 +96,15 @@ public static boolean hasColorDefinitionAsValue(CSSValue value) { return false; } + /** + * Returns whether the value looks like a color definition reference but + * resolves neither to a color, such as an SWT system color, nor to a definition. + */ + public static boolean isUnresolvedColorDefinition(CSSValue value) { + return hasColorDefinitionAsValue(value) && getRGBA(value) == null + && findColorByDefinition(((CssText) value).value()) == null; + } + public static boolean hasColorDefinitionAsValue(String name) { if (name.startsWith(COLOR_DEFINITION_MARKER)) { return !HEX_COLOR_VALUE_PATTERN.matcher(name).matches(); diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTFontHelper.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTFontHelper.java index 854357d7ecb..4d3ba5bccf4 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTFontHelper.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTFontHelper.java @@ -304,6 +304,44 @@ public static CSS2FontProperties resolveRelativeSize(CSS2FontProperties fontProp return resolved; } + /** + * Returns the properties unchanged unless the family references an unresolved + * font definition; then returns the font the element had before it was styled, + * with the size, weight and style set in CSS applied on top. + */ + public static CSS2FontProperties resolveUnresolvedFontDefinition(CSS2FontProperties fontProperties, + CSSElementContext context, Widget widget) { + if (!hasFontDefinitionAsFamily(fontProperties) + || findFontDataByDefinition((CssText) fontProperties.getFamily()).length > 0) { + return fontProperties; + } + // not the current font, which may carry styling of another state such as :selected + FontData baseFontData = getBaseFontData(context); + if (baseFontData == null) { + baseFontData = getFirstFontData(getFont(widget)); + } + if (baseFontData == null) { + return fontProperties; + } + int baseStyle = baseFontData.getStyle(); + CSS2FontProperties resolved = new CSS2FontPropertiesImpl(); + resolved.setFamily(new CssText(CssText.Kind.STRING, baseFontData.getName())); + resolved.setSize(new CssDimension(baseFontData.getHeight(), CssUnit.PT)); + resolved.setSizeFromCSS(true); + resolved.setWeight(new CssText(CssText.Kind.IDENT, (baseStyle & SWT.BOLD) != 0 ? "bold" : "normal")); + resolved.setStyle(new CssText(CssText.Kind.IDENT, (baseStyle & SWT.ITALIC) != 0 ? "italic" : "normal")); + if (fontProperties.isSizeFromCSS()) { + resolved.setSize(fontProperties.getSize()); + } + if (fontProperties.getWeight() != null) { + resolved.setWeight(fontProperties.getWeight()); + } + if (fontProperties.getStyle() != null) { + resolved.setStyle(fontProperties.getStyle()); + } + return resolved; + } + private static boolean isRelativeSize(CssPrimitive size) { if (isKeyword(size, "larger") || isKeyword(size, "smaller")) { return true; diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/converters/CSSValueSWTColorConverterImpl.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/converters/CSSValueSWTColorConverterImpl.java index 0c7d50c8e4d..043534be7f7 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/converters/CSSValueSWTColorConverterImpl.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/converters/CSSValueSWTColorConverterImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2014 Angelo Zerr and others. + * Copyright (c) 2008, 2026 Angelo Zerr and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -44,6 +44,10 @@ public CSSValueSWTColorConverterImpl() { @Override public Color convert(CSSValue value, CSSEngine engine, Object context) throws DOMException { + if (CSSSWTColorHelper.isUnresolvedColorDefinition(value)) { + // treated like unset, so the widget keeps its default color instead of black + return null; + } Display display = (Display) context; Color color = CSSSWTColorHelper.getSWTColor(value, display); if (color == null) { diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyBackgroundSWTHandler.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyBackgroundSWTHandler.java index 35267a7575f..4f2b2d69d49 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyBackgroundSWTHandler.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyBackgroundSWTHandler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2015 Angelo Zerr and others. + * Copyright (c) 2008, 2026 Angelo Zerr and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -86,6 +86,11 @@ public void applyCSSPropertyBackgroundColor(Object element, CSSValue value, if (grad == null) { return; // warn? } + if (grad.getRGBs().isEmpty()) { + // no stop resolved, treated like unset + resetGradient(widget, pseudo); + return; + } if (widget instanceof CTabItem) { CTabFolder folder = ((CTabItem) widget).getParent(); Color[] colors = CSSSWTColorHelper.getSWTColors(grad, @@ -105,6 +110,20 @@ public void applyCSSPropertyBackgroundColor(Object element, CSSValue value, } } + private static void resetGradient(Widget widget, String pseudo) { + if (widget instanceof CTabItem item) { + if ("selected".equals(pseudo)) { + item.getParent().setSelectionBackground(null, null, true); + } else { + item.getParent().setBackground(null, null, true); + } + } else if (widget instanceof Control control) { + GradientBackgroundListener.remove(control); + CSSSWTColorHelper.setBackground(control, null); + CompositeElement.setBackgroundOverriddenByCSSMarker(widget); + } + } + @Override public void applyCSSPropertyBackgroundImage(Object element, CSSValue value, String pseudo, CSSEngine engine) throws Exception { diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyFontSWTHandler.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyFontSWTHandler.java index 157ed7dc9dc..95e8ce04dea 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyFontSWTHandler.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyFontSWTHandler.java @@ -74,11 +74,13 @@ private static void setFont(Widget widget, Font font) { /** * Resolves a relative font size against the font the element had before it was - * styled, so that reapplying a style does not scale an already scaled font. + * styled, and an unresolved font definition against the widget's own font. */ - private static CSS2FontProperties resolveRelativeSize(CSS2FontProperties fontProperties, - CSSElementContext context) { - return CSSSWTFontHelper.resolveRelativeSize(fontProperties, CSSSWTFontHelper.getBaseFontData(context)); + private static CSS2FontProperties resolve(CSS2FontProperties fontProperties, CSSElementContext context, + Widget widget) { + CSS2FontProperties resolved = CSSSWTFontHelper.resolveRelativeSize(fontProperties, + CSSSWTFontHelper.getBaseFontData(context)); + return CSSSWTFontHelper.resolveUnresolvedFontDefinition(resolved, context, widget); } private static void updateChildrenFonts(CTabFolder folder, Font font) { @@ -234,7 +236,7 @@ public void onAllCSSPropertiesApplied(Object element, CSSEngine engine) if (fontProperties == null) { return; } - Font font = (Font) engine.convert(resolveRelativeSize(fontProperties, context), Font.class, widget); + Font font = (Font) engine.convert(resolve(fontProperties, context, widget), Font.class, widget); setFont(widget, font); } @@ -323,7 +325,7 @@ private void applyStyles(CSSStyleDeclaration styleDeclaration, try { // set the font Font font = (Font) engine.convert( - resolveRelativeSize(fontProperties, engine.getCSSElementContext(item)), + resolve(fontProperties, engine.getCSSElementContext(item), item), Font.class, item); setFont(item, font); } catch (Exception e) { diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/custom/CSSPropertye4SelectedTabFillHandler.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/custom/CSSPropertye4SelectedTabFillHandler.java index f01c10cec6b..4e80c492816 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/custom/CSSPropertye4SelectedTabFillHandler.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/custom/CSSPropertye4SelectedTabFillHandler.java @@ -46,12 +46,9 @@ protected void applyCSSProperty(Control control, String property, if (value instanceof CssPrimitive) { Color newColor = (Color) engine.convert(value, Color.class, control.getDisplay()); - if (newColor == null) { - return; - } - if (SWT_SELECTED_TAB_HIGHLIGHT.equals(property)) { - if ("none".equalsIgnoreCase(value.getCssText()) || "transparent".equalsIgnoreCase(value.getCssText())) { + if (newColor == null || "none".equalsIgnoreCase(value.getCssText()) + || "transparent".equalsIgnoreCase(value.getCssText())) { ((ICTabRendering) renderer).setSelectedTabHighlight(null); } else { ((ICTabRendering) renderer).setSelectedTabHighlight(newColor); @@ -59,13 +56,20 @@ protected void applyCSSProperty(Control control, String property, } else if (SWT_SELECTED_HIGHLIGHT_TOP.equals(property)) { Boolean drawHiglightOnTop = (Boolean) engine.convert(value, Boolean.class, control.getDisplay()); ((ICTabRendering) renderer).setSelectedTabHighlightTop(drawHiglightOnTop); + } else if (newColor == null) { + // unresolved definition, treated like unset + ((ICTabRendering) renderer).setSelectedTabFill(null, null); } else { ((ICTabRendering) renderer).setSelectedTabFill(newColor); } } else if (value instanceof CssList) { Gradient grad = (Gradient) engine.convert(value, Gradient.class, control.getDisplay()); - if (grad == null || grad.getRGBs().isEmpty()) { + if (grad == null) { + return; + } + if (grad.getRGBs().isEmpty()) { + ((ICTabRendering) renderer).setSelectedTabFill(null, null); return; } Color[] colors = CSSSWTColorHelper.getSWTColors(grad, diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/internal/css/swt/ColorAndFontUtil.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/internal/css/swt/ColorAndFontUtil.java index 67c6b85b4e8..90e0e54d095 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/internal/css/swt/ColorAndFontUtil.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/internal/css/swt/ColorAndFontUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010, 2019 IBM Corporation and others. + * Copyright (c) 2010, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -14,38 +14,34 @@ package org.eclipse.e4.ui.internal.css.swt; import org.eclipse.e4.ui.internal.css.swt.definition.IColorAndFontProvider; +import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; public class ColorAndFontUtil { - static IColorAndFontProvider colorAndFontProvider = null; + private static final IColorAndFontProvider DEFAULT_PROVIDER = new JFaceColorAndFontProvider(); - static { - if (FrameworkUtil.getBundle(ColorAndFontUtil.class).getBundleContext() != null) { - ServiceTracker colorAndFontProviderTracker = new ServiceTracker<>( - FrameworkUtil.getBundle(ColorAndFontUtil.class).getBundleContext(), - IColorAndFontProvider.class.getName(), null) { - @Override - public IColorAndFontProvider addingService(ServiceReference reference) { - // this is needed so that the unit test can exchange the color and font provider - // with a mocked version - colorAndFontProvider = super.addingService(reference); - return colorAndFontProvider; - } - }; - colorAndFontProviderTracker.open(); + private static final ServiceTracker TRACKER = openTracker(); + + private static ServiceTracker openTracker() { + BundleContext context = FrameworkUtil.getBundle(ColorAndFontUtil.class).getBundleContext(); + if (context == null) { + return null; } + ServiceTracker tracker = new ServiceTracker<>(context, + IColorAndFontProvider.class, null); + tracker.open(); + return tracker; } /** - * Util method to access the OSGI immediate component IColorAndFontProvider - * defined in the same bundle as we do life in the same bundle we do not track - * its life-cycle (as it is the same as our life-cycle) + * Returns the registered {@link IColorAndFontProvider} service, or a provider + * backed by the JFace registries if none is registered. */ public static IColorAndFontProvider getColorAndFontProvider() { - return colorAndFontProvider; + IColorAndFontProvider provider = TRACKER != null ? TRACKER.getService() : null; + return provider != null ? provider : DEFAULT_PROVIDER; } } diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/ColorAndFontProviderImpl.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/internal/css/swt/JFaceColorAndFontProvider.java similarity index 53% rename from bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/ColorAndFontProviderImpl.java rename to bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/internal/css/swt/JFaceColorAndFontProvider.java index 783b05dab3b..b8ab6ed10a8 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/ColorAndFontProviderImpl.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/internal/css/swt/JFaceColorAndFontProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2016 IBM Corporation and others. + * Copyright (c) 2013, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,34 +10,30 @@ * * Contributors: * IBM Corporation - initial API and implementation - ******************************************************************************/ - -package org.eclipse.ui.internal.themes; + *******************************************************************************/ +package org.eclipse.e4.ui.internal.css.swt; import org.eclipse.e4.ui.internal.css.swt.definition.IColorAndFontProvider; +import org.eclipse.jface.resource.FontRegistry; +import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.RGB; -import org.eclipse.ui.internal.Workbench; -import org.eclipse.ui.themes.ITheme; -import org.osgi.service.component.annotations.Component; /** - * @since 3.5 + * Resolves color and font definitions from the JFace registries, which the + * workbench keeps in sync with its current theme. */ -@Component -public class ColorAndFontProviderImpl implements IColorAndFontProvider { +class JFaceColorAndFontProvider implements IColorAndFontProvider { @Override public FontData[] getFont(String symbolicName) { - return getCurrentTheme().getFontRegistry().getFontData(symbolicName); + FontRegistry registry = JFaceResources.getFontRegistry(); + // getFontData returns the default font for unknown keys + return registry.hasValueFor(symbolicName) ? registry.getFontData(symbolicName) : null; } @Override public RGB getColor(String symbolicName) { - return getCurrentTheme().getColorRegistry().getRGB(symbolicName); - } - - private ITheme getCurrentTheme() { - return Workbench.getInstance().getThemeManager().getCurrentTheme(); + return JFaceResources.getColorRegistry().getRGB(symbolicName); } } diff --git a/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF index d445c136917..d78d2f6ab88 100644 --- a/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF @@ -139,5 +139,4 @@ Automatic-Module-Name: org.eclipse.ui.workbench Service-Component: OSGI-INF/org.eclipse.ui.internal.BindingToModelProcessor.xml, OSGI-INF/org.eclipse.ui.internal.CommandToModelProcessor.xml, OSGI-INF/org.eclipse.ui.internal.ContextToModelProcessor.xml, - OSGI-INF/org.eclipse.ui.internal.WindowsDefenderConfigurator.xml, - OSGI-INF/org.eclipse.ui.internal.themes.ColorAndFontProviderImpl.xml + OSGI-INF/org.eclipse.ui.internal.WindowsDefenderConfigurator.xml diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/WorkbenchThemeManager.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/WorkbenchThemeManager.java index 691725a4665..fe445d865ad 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/WorkbenchThemeManager.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/themes/WorkbenchThemeManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2017 IBM Corporation and others. + * Copyright (c) 2004, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -96,12 +96,13 @@ public static synchronized void disposeManager() { private ITheme currentTheme; private final IPropertyChangeListener currentThemeListener = event -> { - firePropertyChange(event); + // JFace first, as the CSS engine resolves definitions from the JFace registries if (event.getSource() instanceof FontRegistry) { JFaceResources.getFontRegistry().put(event.getProperty(), (FontData[]) event.getNewValue()); } else if (event.getSource() instanceof ColorRegistry) { JFaceResources.getColorRegistry().put(event.getProperty(), (RGB) event.getNewValue()); } + firePropertyChange(event); }; private ColorRegistry defaultThemeColorRegistry; @@ -337,7 +338,6 @@ public void setCurrentTheme(String id) { init(); ITheme oldTheme = currentTheme; if (WorkbenchThemeManager.getInstance().doSetCurrentTheme(id)) { - firePropertyChange(CHANGE_CURRENT_THEME, oldTheme, getCurrentTheme()); if (oldTheme != null) { oldTheme.removePropertyChangeListener(currentThemeListener); } @@ -361,6 +361,7 @@ public void setCurrentTheme(String id) { for (String key : themeFonts.getKeySet()) { jfaceFonts.put(key, themeFonts.getFontData(key)); } + firePropertyChange(CHANGE_CURRENT_THEME, oldTheme, getCurrentTheme()); if (oldTheme != null && eventBroker != null) { eventBroker.send(UIEvents.UILifeCycle.THEME_CHANGED, null); eventBroker.send(UIEvents.UILifeCycle.THEME_DEFINITION_CHANGED, context.get(MApplication.class)); diff --git a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTHelperTestCase.java b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTHelperTestCase.java index da849dd1916..779e1eacb96 100644 --- a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTHelperTestCase.java +++ b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/css/swt/helpers/CSSSWTHelperTestCase.java @@ -19,6 +19,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import java.util.ArrayList; import java.util.List; import org.eclipse.e4.ui.css.core.css2.CSS2FontHelper; @@ -32,13 +33,22 @@ import org.eclipse.e4.ui.internal.css.swt.definition.IColorAndFontProvider; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.RGB; +import org.junit.jupiter.api.AfterEach; import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; public abstract class CSSSWTHelperTestCase { protected static final String CSS_ITALIC = CSS2FontHelper.getFontStyle(true); protected static final String CSS_BOLD = CSS2FontHelper.getFontWeight(true); + private final List> registrations = new ArrayList<>(); + + @AfterEach + void unregisterProviders() { + registrations.forEach(ServiceRegistration::unregister); + } + protected void registerFontProviderWith(String expectedSymbolicName, String family, int size, int style) { IColorAndFontProvider provider = mock(IColorAndFontProvider.class); doReturn(new FontData[] { new FontData(family, size, style) }).when(provider).getFont(expectedSymbolicName); @@ -49,12 +59,11 @@ protected void registerColorProviderWith(String expectedSymbolicName, RGB rgb) { IColorAndFontProvider provider = mock(IColorAndFontProvider.class); doReturn(rgb).when(provider).getColor(expectedSymbolicName); registerProvider(provider); - } private void registerProvider(final IColorAndFontProvider provider) { - FrameworkUtil.getBundle(getClass()).getBundleContext().registerService(IColorAndFontProvider.class, provider, - null); + registrations.add(FrameworkUtil.getBundle(getClass()).getBundleContext() + .registerService(IColorAndFontProvider.class, provider, null)); } protected CSS2FontProperties fontProperties(String family) { diff --git a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabItemTest.java b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabItemTest.java index 15d02d3b6a1..b7145f76bc5 100644 --- a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabItemTest.java +++ b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabItemTest.java @@ -27,6 +27,7 @@ import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.layout.FillLayout; @@ -113,6 +114,38 @@ protected CTabFolder createTestTabFolder(String styleSheet, boolean open) { return folder; } + @Test + void testUnresolvedFontDefinitionKeepsHeightOfEachItem() { + CTabFolder folder = createTestTabFolder(false); + CTabItem small = folder.getItem(0); + CTabItem large = folder.getItem(1); + Font smallFont = new Font(css.getDisplay(), "Arial", 9, SWT.NORMAL); + Font largeFont = new Font(css.getDisplay(), "Arial", 14, SWT.NORMAL); + small.setFont(smallFont); + large.setFont(largeFont); + CSSEngine engine = css.createEngine("CTabItem { font-family: '#UNKNOWN-FONT-DEFINITION'; }"); + + engine.applyStyles(folder.getShell(), true); + shell.open(); + spinEventLoop(); + + assertEquals(9, small.getFont().getFontData()[0].getHeight()); + assertEquals(14, large.getFont().getFontData()[0].getHeight()); + shell.dispose(); + smallFont.dispose(); + largeFont.dispose(); + } + + @Test + void testUnresolvedFontDefinitionDropsSelectedStyleOnDeselect() { + CTabFolder folder = createTestTabFolder("CTabItem { font-family: '#UNKNOWN-FONT-DEFINITION'; }\n" + + "CTabItem:selected { font-weight: bold; }"); + spinEventLoop(); + for (int i = 0; i < folder.getItemCount(); i++) { + testSelectedFontBold(folder, i); + } + } + @Test void testFontRegular() { CTabFolder folder = createTestTabFolder("Button { font-family: Verdana; font-size: 12 }\n" diff --git a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/ColorDefinitionTest.java b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/ColorDefinitionTest.java index 8e607614877..d91af881182 100644 --- a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/ColorDefinitionTest.java +++ b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/ColorDefinitionTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2019 IBM Corporation and others. + * Copyright (c) 2013, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -17,14 +17,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import java.util.Hashtable; - import org.eclipse.e4.ui.css.core.engine.CSSEngine; +import org.eclipse.e4.ui.internal.css.swt.ColorAndFontUtil; import org.eclipse.e4.ui.internal.css.swt.definition.IColorAndFontProvider; +import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; @@ -33,15 +34,26 @@ import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.internal.themes.ColorDefinition; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; public class ColorDefinitionTest { @RegisterExtension CssSwtEngine css = new CssSwtEngine(); + private ServiceRegistration registration; + + @AfterEach + void unregisterProvider() { + if (registration != null) { + registration.unregister(); + } + } + @Test void testColorDefinition() { //given @@ -150,6 +162,61 @@ void testUnset() { shell.dispose(); } + @Test + void testUnresolvedColorDefinitionKeepsDefaultColor() { + Display display = css.getDisplay(); + CSSEngine engine = css.createEngine("Button {background-color: '#UNKNOWN-COLOR-DEFINITION';}"); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + Button button = new Button(shell, SWT.NONE); + RGB defaultBackground = button.getBackground().getRGB(); + + engine.applyStyles(button, true); + + assertEquals(defaultBackground, button.getBackground().getRGB()); + engine.dispose(); + shell.dispose(); + } + + @Test + void testColorDefinitionFromJFaceRegistryWithoutProvider() { + JFaceResources.getColorRegistry().put("CSS_TEST_JFACE_COLOR", new RGB(1, 2, 3)); + Display display = css.getDisplay(); + CSSEngine engine = css.createEngine("Label {background-color: '#CSS_TEST_JFACE_COLOR'}"); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + Label label = new Label(shell, SWT.NONE); + + engine.applyStyles(label, true); + + assertEquals(new RGB(1, 2, 3), label.getBackground().getRGB()); + engine.dispose(); + shell.dispose(); + } + + @Test + void testQuotedSystemColorIsNotTreatedAsUnresolvedDefinition() { + Display display = css.getDisplay(); + CSSEngine engine = css.createEngine("Label {background-color: '#COLOR-LIST-SELECTION'}"); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + Label label = new Label(shell, SWT.NONE); + + engine.applyStyles(label, true); + + assertEquals(display.getSystemColor(SWT.COLOR_LIST_SELECTION).getRGB(), label.getBackground().getRGB()); + engine.dispose(); + shell.dispose(); + } + + @Test + void testUnregisteredColorProviderIsNoLongerUsed() { + registerColorProviderWith("ACTIVE_HYPERLINK_COLOR", new RGB(255, 0, 0)); + IColorAndFontProvider provider = ColorAndFontUtil.getColorAndFontProvider(); + + registration.unregister(); + registration = null; + + assertNotSame(provider, ColorAndFontUtil.getColorAndFontProvider()); + } + @Test void testSetColorDefinitionWithSystemColor() { // given @@ -178,10 +245,7 @@ private ColorDefinition colorDefinition(String uniqueId, String name, private void registerColorProviderWith(final String symbolicName, final RGB rgb) { IColorAndFontProvider provider = mock(IColorAndFontProvider.class); doReturn(rgb).when(provider).getColor(symbolicName); - Hashtable properties = new Hashtable<>(); - properties.put("service.ranking", "1000"); - - FrameworkUtil.getBundle(getClass()).getBundleContext().registerService(IColorAndFontProvider.class, provider, - properties); + registration = FrameworkUtil.getBundle(getClass()).getBundleContext() + .registerService(IColorAndFontProvider.class, provider, null); } } diff --git a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/FontDefinitionTest.java b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/FontDefinitionTest.java index 2d143e8c6da..40ae84cb207 100644 --- a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/FontDefinitionTest.java +++ b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/FontDefinitionTest.java @@ -19,13 +19,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import java.util.Hashtable; - import org.eclipse.e4.ui.css.core.engine.CSSEngine; import org.eclipse.e4.ui.internal.css.swt.definition.IColorAndFontProvider; +import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; @@ -33,15 +33,26 @@ import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.internal.themes.FontDefinition; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; public class FontDefinitionTest { @RegisterExtension CssSwtEngine css = new CssSwtEngine(); + private ServiceRegistration registration; + + @AfterEach + void unregisterProvider() { + if (registration != null) { + registration.unregister(); + } + } + @Test void testFontDefinition() { //given @@ -154,6 +165,85 @@ void testWidgetWithFontDefinitionAsFontFamily() { font.dispose(); } + @Test + void testFontDefinitionFromJFaceRegistryWithoutProvider() { + JFaceResources.getFontRegistry().put("CSS_TEST_JFACE_FONT", + new FontData[] { new FontData("Times", 13, SWT.NORMAL) }); + Display display = css.getDisplay(); + CSSEngine engine = css.createEngine("Label {font-family: '#CSS_TEST_JFACE_FONT'}"); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + Label label = new Label(shell, SWT.NONE); + + engine.applyStyles(label, true); + + assertEquals("Times", label.getFont().getFontData()[0].getName()); + assertEquals(13, label.getFont().getFontData()[0].getHeight()); + shell.dispose(); + } + + @Test + void testUnresolvedFontDefinitionKeepsWidgetFont() { + Display display = css.getDisplay(); + CSSEngine engine = css.createEngine("Label {font-family: '#UNKNOWN-FONT-DEFINITION'}"); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + Label label = new Label(shell, SWT.NONE); + Font font = new Font(display, "DejaVu Sans", 9, SWT.BOLD); + label.setFont(font); + String fontName = font.getFontData()[0].getName(); + + engine.applyStyles(label, true); + + assertEquals(fontName, label.getFont().getFontData()[0].getName()); + assertEquals(9, label.getFont().getFontData()[0].getHeight()); + assertEquals(SWT.BOLD, label.getFont().getFontData()[0].getStyle()); + shell.dispose(); + font.dispose(); + } + + @Test + void testUnresolvedFontDefinitionAppliesOtherFontAttributes() { + Display display = css.getDisplay(); + CSSEngine engine = css.createEngine( + "Label {font-family: '#UNKNOWN-FONT-DEFINITION'; font-size: 14pt; font-style: italic}"); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + Label label = new Label(shell, SWT.NONE); + Font font = new Font(display, "Arial", 9, SWT.BOLD); + label.setFont(font); + + engine.applyStyles(label, true); + + assertEquals("Arial", label.getFont().getFontData()[0].getName()); + assertEquals(14, label.getFont().getFontData()[0].getHeight()); + assertEquals(SWT.BOLD | SWT.ITALIC, label.getFont().getFontData()[0].getStyle()); + shell.dispose(); + font.dispose(); + } + + @Test + void testUnresolvedFontDefinitionKeepsFamilyOfEachWidget() { + Display display = css.getDisplay(); + CSSEngine engine = css.createEngine("Label {font-family: '#UNKNOWN-FONT-DEFINITION'}"); + Shell shell = new Shell(display, SWT.SHELL_TRIM); + Label arialLabel = new Label(shell, SWT.NONE); + Font arial = new Font(display, "Arial", 9, SWT.BOLD); + arialLabel.setFont(arial); + Label timesLabel = new Label(shell, SWT.NONE); + Font times = new Font(display, "Times", 9, SWT.BOLD); + timesLabel.setFont(times); + String arialName = arialLabel.getFont().getFontData()[0].getName(); + String timesName = timesLabel.getFont().getFontData()[0].getName(); + assumeFalse(arialName.equals(timesName)); + + engine.applyStyles(arialLabel, true); + engine.applyStyles(timesLabel, true); + + assertEquals(arialName, arialLabel.getFont().getFontData()[0].getName()); + assertEquals(timesName, timesLabel.getFont().getFontData()[0].getName()); + shell.dispose(); + arial.dispose(); + times.dispose(); + } + private FontDefinition fontDefinition(String uniqueId, String name, String categoryId, String description) { return new FontDefinition(new FontDefinition(name, uniqueId, @@ -164,11 +254,7 @@ private FontDefinition fontDefinition(String uniqueId, String name, private void registerFontProviderWith(final String symbolicName, final FontData fontData) { IColorAndFontProvider provider = mock(IColorAndFontProvider.class); doReturn(new FontData[] { fontData }).when(provider).getFont(symbolicName); - - Hashtable properties = new Hashtable<>(); - properties.put("service.ranking", "1000"); - - FrameworkUtil.getBundle(getClass()).getBundleContext().registerService(IColorAndFontProvider.class, provider, - null); + registration = FrameworkUtil.getBundle(getClass()).getBundleContext() + .registerService(IColorAndFontProvider.class, provider, null); } } diff --git a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/GradientTest.java b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/GradientTest.java index 484bef03783..05d04f0d75b 100644 --- a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/GradientTest.java +++ b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/GradientTest.java @@ -16,12 +16,21 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockingDetails; +import static org.mockito.Mockito.withSettings; import java.lang.reflect.Field; import org.eclipse.e4.ui.css.core.engine.CSSEngine; +import org.eclipse.e4.ui.css.swt.properties.custom.CSSPropertye4SelectedTabFillHandler; +import org.eclipse.e4.ui.css.swt.properties.css2.CSSPropertyBackgroundSWTHandler; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; +import org.eclipse.swt.custom.CTabFolderRenderer; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; @@ -125,6 +134,59 @@ void testBadPercents() { // percent } + @Test + void testBackgroundGradientWithOnlyUnresolvedDefinitionsIsUnset() throws Exception { + CSSEngine engine = css.createEngine(""); + Shell shell = new Shell(css.getDisplay(), SWT.SHELL_TRIM); + Composite composite = new Composite(shell, SWT.NONE); + composite.setSize(100, 100); + CSSPropertyBackgroundSWTHandler handler = new CSSPropertyBackgroundSWTHandler(); + handler.applyCSSPropertyBackgroundColor(engine.getElement(composite), + engine.parsePropertyValue("#FF0000 #0000FF 100%"), null, engine); + assertNotNull(composite.getBackgroundImage()); + + handler.applyCSSPropertyBackgroundColor(engine.getElement(composite), + engine.parsePropertyValue("'#MISSING-A' '#MISSING-B' 100%"), null, engine); + + assertNull(composite.getBackgroundImage()); + shell.dispose(); + } + + @Test + void testTabGradientWithOnlyUnresolvedDefinitionsIsUnset() throws Exception { + CTabFolder folderToTest = createTestCTabFolder("CTabItem:selected { background-color: #FF0000 #0000FF}"); + CSSEngine engine = css.createEngine(""); + + new CSSPropertyBackgroundSWTHandler().applyCSSPropertyBackgroundColor( + engine.getElement(folderToTest.getItem(0)), + engine.parsePropertyValue("'#MISSING-A' '#MISSING-B' 100%"), "selected", engine); + + assertNull(getSelectionGradientPercents(folderToTest)); + } + + @Test + void testSelectedTabFillWithUnresolvedDefinitionResetsFill() throws Exception { + CTabFolder folder = createTestCTabFolder(""); + CSSEngine engine = css.createEngine(""); + Class renderingType = Class.forName("org.eclipse.e4.ui.internal.css.swt.ICTabRendering"); + CTabFolderRenderer renderer = mock(CTabFolderRenderer.class, withSettings().extraInterfaces(renderingType)); + folder.setRenderer(renderer); + CSSPropertye4SelectedTabFillHandler handler = new CSSPropertye4SelectedTabFillHandler(); + + for (String value : new String[] { "'#MISSING-A'", "'#MISSING-A' '#MISSING-B' 100%" }) { + handler.applyCSSProperty(engine.getElement(folder), "swt-selected-tab-fill", engine.parsePropertyValue(value), + null, engine); + } + + long resets = mockingDetails(renderer).getInvocations().stream() + .filter(i -> i.getMethod().getName().equals("setSelectedTabFill") + && i.getArguments().length == 2 && i.getArguments()[0] == null && i.getArguments()[1] == null) + .count(); + assertEquals(2, resets); + assertTrue(mockingDetails(renderer).getInvocations().stream() + .noneMatch(i -> i.getMethod().getName().equals("setSelectedTabFill") && i.getArguments().length == 1)); + } + /* * Test to ensure that one bad apple doesn't spoil it for everyone * (non-existent color name is ignored) diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/themes/JFaceThemeTest.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/themes/JFaceThemeTest.java index a73518e2065..28137a2c6f5 100644 --- a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/themes/JFaceThemeTest.java +++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/themes/JFaceThemeTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2007 IBM Corporation and others. + * Copyright (c) 2004, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -16,15 +16,22 @@ import static org.eclipse.ui.PlatformUI.getWorkbench; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.e4.ui.internal.css.swt.ColorAndFontUtil; +import org.eclipse.e4.ui.internal.css.swt.definition.IColorAndFontProvider; import org.eclipse.jface.resource.ColorDescriptor; import org.eclipse.jface.resource.ColorRegistry; import org.eclipse.jface.resource.FontRegistry; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.ui.themes.ITheme; @@ -83,6 +90,74 @@ public void testPushdown() { assertEquals(10, listener.getEvents().size()); } + @Test + public void testColorAndFontProviderResolvesCurrentTheme() { + IColorAndFontProvider provider = ColorAndFontUtil.getColorAndFontProvider(); + assertEquals(new RGB(1, 1, 2), provider.getColor("rgbcolor")); + + fManager.setCurrentTheme(THEME1); + assertEquals(new RGB(2, 1, 1), provider.getColor("rgbcolor")); + + ITheme theme = fManager.getCurrentTheme(); + ColorRegistry themeColors = theme.getColorRegistry(); + for (String key : themeColors.getKeySet()) { + assertEquals(themeColors.getRGB(key), provider.getColor(key)); + } + FontRegistry themeFonts = theme.getFontRegistry(); + for (String key : themeFonts.getKeySet()) { + assertArrayEquals(themeFonts.getFontData(key), provider.getFont(key)); + } + } + + @Test + public void testColorAndFontProviderIsUpdatedBeforeThemeListeners() { + IColorAndFontProvider provider = ColorAndFontUtil.getColorAndFontProvider(); + List seen = new ArrayList<>(); + IPropertyChangeListener listener = new IPropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent event) { + seen.add(provider.getColor("rgbcolor")); + } + }; + fManager.addPropertyChangeListener(listener); + try { + fManager.setCurrentTheme(THEME1); + } finally { + fManager.removePropertyChangeListener(listener); + } + + assertFalse(seen.isEmpty()); + for (RGB rgb : seen) { + assertEquals(new RGB(2, 1, 1), rgb); + } + } + + @Test + public void testColorAndFontProviderIsUpdatedBeforeThemeValueListeners() { + IColorAndFontProvider provider = ColorAndFontUtil.getColorAndFontProvider(); + ColorRegistry themeColors = fManager.getCurrentTheme().getColorRegistry(); + RGB original = themeColors.getRGB("rgbcolor"); + List seen = new ArrayList<>(); + IPropertyChangeListener listener = new IPropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent event) { + seen.add(provider.getColor("rgbcolor")); + } + }; + fManager.addPropertyChangeListener(listener); + try { + themeColors.put("rgbcolor", new RGB(9, 9, 9)); + } finally { + fManager.removePropertyChangeListener(listener); + themeColors.put("rgbcolor", original); + } + + assertFalse(seen.isEmpty()); + for (RGB rgb : seen) { + assertEquals(new RGB(9, 9, 9), rgb); + } + } + /** * Tests to ensure correct behavior of getColorDescriptor methods. */