diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/DecorationResult.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/DecorationResult.java index 4fd2cf797ce..f945956b9b6 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/DecorationResult.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/DecorationResult.java @@ -14,7 +14,6 @@ package org.eclipse.ui.internal.decorators; import java.util.List; -import java.util.ListIterator; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ResourceManager; @@ -30,9 +29,9 @@ */ public class DecorationResult { - private final List prefixes; + private final List prefixes; - private final List suffixes; + private final List suffixes; private ImageDescriptor[] descriptors; @@ -42,8 +41,8 @@ public class DecorationResult { private final Font font; - DecorationResult(List prefixList, List suffixList, ImageDescriptor[] imageDescriptors, Color resultForegroundColor, - Color resultBackgroundColor, Font resultFont) { + DecorationResult(List prefixList, List suffixList, ImageDescriptor[] imageDescriptors, + Color resultForegroundColor, Color resultBackgroundColor, Font resultFont) { prefixes = prefixList; suffixes = suffixList; @@ -103,18 +102,14 @@ public String decorateWithText(String text) { StringBuilder result = new StringBuilder(); - ListIterator prefixIterator = prefixes.listIterator(); - - while (prefixIterator.hasNext()) { - result.append(prefixIterator.next()); + for (String prefix : prefixes) { + result.append(prefix); } result.append(text); - ListIterator suffixIterator = suffixes.listIterator(); - - while (suffixIterator.hasNext()) { - result.append(suffixIterator.next()); + for (String suffix : suffixes) { + result.append(suffix); } return result.toString(); @@ -134,7 +129,7 @@ ImageDescriptor[] getDescriptors() { * * @return List */ - List getPrefixes() { + List getPrefixes() { return prefixes; } @@ -143,7 +138,7 @@ List getPrefixes() { * * @return List */ - List getSuffixes() { + List getSuffixes() { return suffixes; } diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/DecoratorManager.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/DecoratorManager.java index 608bb538f84..058b878516a 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/DecoratorManager.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/DecoratorManager.java @@ -16,9 +16,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -import java.util.Iterator; import java.util.Set; -import java.util.StringTokenizer; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; @@ -35,6 +33,7 @@ import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; +import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.viewers.DecorationContext; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IColorDecorator; @@ -60,6 +59,7 @@ import org.eclipse.ui.internal.util.PrefUtil; import org.eclipse.ui.internal.util.Util; import org.eclipse.ui.progress.WorkbenchJob; +import org.eclipse.ui.themes.IThemeManager; /** * The DecoratorManager is the class that handles all of the decorators defined @@ -104,6 +104,8 @@ public class DecoratorManager implements ILabelProviderListener, IDecoratorManag private LocalResourceManager resourceManager; + private IPropertyChangeListener themeListener; + /** * ManagedWorkbenchLabelDecorator is the internal LabelDecorator passed as * result of calls to {@link IDecoratorManager#getLabelDecorator()} @@ -225,6 +227,46 @@ public DecoratorManager() { */ public void schedule() { scheduler.schedule(); + installThemeListener(); + } + + /** + * Install a listener on the workbench theme manager so that decoration results + * that cached colors or fonts from the theme registries get invalidated when a + * theme-related property changes. {@link IThemeManager} forwards property + * changes from the current theme's color and font registries and fires + * {@link IThemeManager#CHANGE_CURRENT_THEME} on theme switches, which together + * cover both the workbench-startup race and runtime theme switches. + */ + private void installThemeListener() { + if (themeListener != null || !PlatformUI.isWorkbenchRunning()) { + return; + } + themeListener = event -> handleThemeChange(); + PlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener(themeListener); + } + + private void removeThemeListener() { + if (themeListener == null || !PlatformUI.isWorkbenchRunning()) { + themeListener = null; + return; + } + PlatformUI.getWorkbench().getThemeManager().removePropertyChangeListener(themeListener); + themeListener = null; + } + + /** + * Invalidate decoration results and notify viewers when theme colors or fonts + * change. Cached {@link DecorationResult}s hold {@link Color} and {@link Font} + * references obtained from the theme registries at decoration time, and those + * references can outlive the theme that produced them. + */ + private void handleThemeChange() { + if (!PlatformUI.isWorkbenchRunning()) { + return; + } + scheduler.clearResults(); + fireListenersInUIThread(new LabelProviderChangedEvent(this)); } /** @@ -236,10 +278,8 @@ private void initializeDecoratorDefinitions() { ArrayList full = new ArrayList<>(); ArrayList lightweight = new ArrayList<>(); - Iterator allDefinitions = values.iterator(); IExtensionTracker configurationElementTracker = PlatformUI.getWorkbench().getExtensionTracker(); - while (allDefinitions.hasNext()) { - DecoratorDefinition nextDefinition = allDefinitions.next(); + for (DecoratorDefinition nextDefinition : values) { if (nextDefinition.isFull()) { full.add(nextDefinition); } else { @@ -250,12 +290,10 @@ private void initializeDecoratorDefinitions() { nextDefinition, IExtensionTracker.REF_WEAK); } - fullDefinitions = new FullDecoratorDefinition[full.size()]; - full.toArray(fullDefinitions); + fullDefinitions = full.toArray(new FullDecoratorDefinition[0]); - LightweightDecoratorDefinition[] lightweightDefinitions = new LightweightDecoratorDefinition[lightweight - .size()]; - lightweight.toArray(lightweightDefinitions); + LightweightDecoratorDefinition[] lightweightDefinitions = lightweight + .toArray(new LightweightDecoratorDefinition[0]); lightweightManager = new LightweightDecoratorManager(lightweightDefinitions); @@ -699,14 +737,16 @@ public void applyDecoratorsPreference() { String preferenceValue = WorkbenchPlugin.getDefault().getPreferenceStore() .getString(IPreferenceConstants.ENABLED_DECORATORS); - StringTokenizer tokenizer = new StringTokenizer(preferenceValue, PREFERENCE_SEPARATOR); Set enabledIds = new HashSet<>(); Set disabledIds = new HashSet<>(); - while (tokenizer.hasMoreTokens()) { - String nextValuePair = tokenizer.nextToken(); - - // Strip out the true or false to get the id - String id = nextValuePair.substring(0, nextValuePair.indexOf(VALUE_SEPARATOR)); + for (String nextValuePair : preferenceValue.split(PREFERENCE_SEPARATOR)) { + // Skip empty tokens (leading or consecutive separators) and + // malformed entries without a VALUE_SEPARATOR. + int separatorIndex = nextValuePair.indexOf(VALUE_SEPARATOR); + if (separatorIndex == -1) { + continue; + } + String id = nextValuePair.substring(0, separatorIndex); if (nextValuePair.endsWith(P_TRUE)) { enabledIds.add(id); } else { @@ -740,6 +780,7 @@ public void applyDecoratorsPreference() { * dispose() will be called on them. */ public void shutdown() { + removeThemeListener(); scheduler.shutdown(); // Disable all of the enabled decorators // so as to force a dispose of thier decorators diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/LightweightDecoratorManager.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/LightweightDecoratorManager.java index 6b4cd49e2ec..f976fe61d2f 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/LightweightDecoratorManager.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/decorators/LightweightDecoratorManager.java @@ -43,20 +43,7 @@ public class LightweightDecoratorManager extends ObjectContributorManager { private static class LightweightRunnable implements ISafeRunnable { - static class RunnableData { - - final DecorationBuilder builder; - - final LightweightDecoratorDefinition decorator; - - final Object element; - - public RunnableData(Object object, DecorationBuilder builder, LightweightDecoratorDefinition definition) { - this.element = object; - this.builder = builder; - this.decorator = definition; - } - + record RunnableData(Object element, DecorationBuilder builder, LightweightDecoratorDefinition decorator) { boolean isConsistent() { return builder != null && decorator != null && element != null; } @@ -74,7 +61,7 @@ void setValues(Object object, DecorationBuilder builder, LightweightDecoratorDef @Override public void handleException(Throwable exception) { IStatus status = StatusUtil.newStatus(IStatus.ERROR, exception.getMessage(), exception); - LightweightDecoratorDefinition decorator = data.decorator; + LightweightDecoratorDefinition decorator = data.decorator(); String message; if (decorator == null) { message = WorkbenchMessages.DecoratorError; @@ -102,7 +89,7 @@ public void run() throws Exception { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=300358 RunnableData data = this.data; if (data.isConsistent()) { - data.decorator.decorate(data.element, data.builder); + data.decorator().decorate(data.element(), data.builder()); } clearReferences(); } @@ -251,9 +238,7 @@ LightweightDecoratorDefinition[] enabledDefinitions() { result.add(lightweightDefinition); } } - LightweightDecoratorDefinition[] returnArray = new LightweightDecoratorDefinition[result.size()]; - result.toArray(returnArray); - return returnArray; + return result.toArray(new LightweightDecoratorDefinition[0]); } /**