From ee2815f6b3dba21dab66c5624768b7e2afa53649 Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Wed, 8 Apr 2026 12:23:04 +0200 Subject: [PATCH 1/2] Use new SWT Display.setDarkThemePreferred() API for dark theme Replace platform-specific dark theme processors (Win32, GTK, Cocoa) with the new cross-platform SWT API Display.setDarkThemePreferred(boolean). The ThemeEngine now calls display.setDarkThemePreferred() directly when a theme is set, signaling the dark mode preference to the OS for native components like title bars, scrollbars, and native dialogs. In IDEApplication, the manual dark styling (hardcoded colors, SWT.Show listeners, recursive style application) is replaced with a single call to display.setDarkThemePreferred(true) during early startup. Dark theme processor code removed from: - org.eclipse.e4.ui.swt.win32 (DarkThemeProcessor using OS.setTheme) - org.eclipse.e4.ui.swt.gtk (DarkThemeProcessor using OS.setDarkThemePreferred) - org.eclipse.e4.ui.workbench.renderers.swt.cocoa (CocoaDarkThemeProcessor using OS.setTheme) The bundles themselves are kept for later retirement. Fixes https://github.com/eclipse-platform/eclipse.platform.ui/issues/3857 --- .../META-INF/MANIFEST.MF | 2 +- .../css/swt/internal/theme/ThemeEngine.java | 3 + .../META-INF/MANIFEST.MF | 4 -- .../org.eclipse.e4.ui.swt.gtk/fragment.xml | 8 --- .../swt/internal/gtk/DarkThemeProcessor.java | 60 ----------------- .../META-INF/MANIFEST.MF | 4 -- .../org.eclipse.e4.ui.swt.win32/fragment.xml | 8 --- .../internal/win32/DarkThemeProcessor.java | 55 ---------------- .../META-INF/MANIFEST.MF | 1 - .../fragment.xml | 10 --- .../cocoa/CocoaDarkThemeProcessor.java | 64 ------------------- .../ide/application/IDEApplication.java | 32 +++++++++- 12 files changed, 34 insertions(+), 217 deletions(-) delete mode 100644 bundles/org.eclipse.e4.ui.swt.gtk/src/org/eclipse/e4/ui/swt/internal/gtk/DarkThemeProcessor.java delete mode 100644 bundles/org.eclipse.e4.ui.swt.win32/src/org/eclipse/e4/ui/swt/internal/win32/DarkThemeProcessor.java delete mode 100644 bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/src/org/eclipse/e4/ui/swt/internal/cocoa/CocoaDarkThemeProcessor.java diff --git a/bundles/org.eclipse.e4.ui.css.swt.theme/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.css.swt.theme/META-INF/MANIFEST.MF index 779d1ad1941..137edc5df3c 100644 --- a/bundles/org.eclipse.e4.ui.css.swt.theme/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.css.swt.theme/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-21 -Require-Bundle: org.eclipse.swt;bundle-version="[3.133.0,4.0.0)", +Require-Bundle: org.eclipse.swt;bundle-version="[3.134.0,4.0.0)", org.eclipse.e4.ui.css.swt;bundle-version="0.13.100", org.eclipse.e4.ui.css.core;bundle-version="0.12.200", org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)" diff --git a/bundles/org.eclipse.e4.ui.css.swt.theme/src/org/eclipse/e4/ui/css/swt/internal/theme/ThemeEngine.java b/bundles/org.eclipse.e4.ui.css.swt.theme/src/org/eclipse/e4/ui/css/swt/internal/theme/ThemeEngine.java index 8ddcf3d802e..5c7e4724459 100644 --- a/bundles/org.eclipse.e4.ui.css.swt.theme/src/org/eclipse/e4/ui/css/swt/internal/theme/ThemeEngine.java +++ b/bundles/org.eclipse.e4.ui.css.swt.theme/src/org/eclipse/e4/ui/css/swt/internal/theme/ThemeEngine.java @@ -510,6 +510,9 @@ public void setTheme(ITheme theme, boolean restore, boolean force) { ThemeEngineManager.logError(e.getMessage(), e); } } + boolean isDark = theme.getId().contains("dark"); //$NON-NLS-1$ + display.setDarkThemePreferred(isDark); + sendThemeChangeEvent(restore); for (CSSEngine engine : cssEngines) { diff --git a/bundles/org.eclipse.e4.ui.swt.gtk/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.swt.gtk/META-INF/MANIFEST.MF index c8a81005025..e6e792c72f2 100644 --- a/bundles/org.eclipse.e4.ui.swt.gtk/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.swt.gtk/META-INF/MANIFEST.MF @@ -8,8 +8,4 @@ Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-Localization: fragment-gtk Bundle-Vendor: %providerName Eclipse-PlatformFilter: (osgi.ws=gtk) -Import-Package: jakarta.annotation;version="[2.1.0,3.0.0)", - jakarta.inject;version="[2.0.0,3.0.0)", - org.eclipse.e4.core.services.events, - org.osgi.service.event;version="1.3.1" Automatic-Module-Name: org.eclipse.e4.ui.swt.gtk diff --git a/bundles/org.eclipse.e4.ui.swt.gtk/fragment.xml b/bundles/org.eclipse.e4.ui.swt.gtk/fragment.xml index 21607e4994d..79151e0318f 100644 --- a/bundles/org.eclipse.e4.ui.swt.gtk/fragment.xml +++ b/bundles/org.eclipse.e4.ui.swt.gtk/fragment.xml @@ -1,13 +1,5 @@ - - - - diff --git a/bundles/org.eclipse.e4.ui.swt.gtk/src/org/eclipse/e4/ui/swt/internal/gtk/DarkThemeProcessor.java b/bundles/org.eclipse.e4.ui.swt.gtk/src/org/eclipse/e4/ui/swt/internal/gtk/DarkThemeProcessor.java deleted file mode 100644 index 6152674e777..00000000000 --- a/bundles/org.eclipse.e4.ui.swt.gtk/src/org/eclipse/e4/ui/swt/internal/gtk/DarkThemeProcessor.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015, 2016 Red Hat and others. - * - * 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: - * Sopot Cela - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.swt.internal.gtk; - -import org.eclipse.e4.core.services.events.IEventBroker; -import org.eclipse.e4.ui.css.swt.theme.ITheme; -import org.eclipse.e4.ui.css.swt.theme.IThemeEngine; -import org.eclipse.swt.internal.gtk.OS; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.event.EventHandler; - -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; -import jakarta.inject.Inject; - -@SuppressWarnings("restriction") -public class DarkThemeProcessor { - - @Inject - IEventBroker eventBroker; - - private EventHandler eventHandler; - - @PostConstruct - public void intialize() { - - eventHandler = event -> { - if (event == null) { - return; - } - ITheme theme = (ITheme) event.getProperty("theme"); - final boolean isDark = theme.getId().contains("dark"); //$NON-NLS-1$ - Display display = (Display) event.getProperty(IThemeEngine.Events.DEVICE); - - // not using UISynchronize as this is specific to SWT/GTK - // scenarios - display.asyncExec(() -> OS.setDarkThemePreferred(isDark)); - }; - // using the IEventBroker explicitly because the @EventTopic annotation - // is unpredictable with processors within the debugger - eventBroker.subscribe(IThemeEngine.Events.THEME_CHANGED, eventHandler); - } - - @PreDestroy - public void cleanUp() { - eventBroker.unsubscribe(eventHandler); - } - -} diff --git a/bundles/org.eclipse.e4.ui.swt.win32/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.swt.win32/META-INF/MANIFEST.MF index 187d89e9d46..9795fd9215a 100644 --- a/bundles/org.eclipse.e4.ui.swt.win32/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.swt.win32/META-INF/MANIFEST.MF @@ -8,8 +8,4 @@ Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-Localization: fragment-win32 Bundle-Vendor: %providerName Eclipse-PlatformFilter: (osgi.ws=win32) -Import-Package: jakarta.annotation;version="[2.1.0,3.0.0)", - jakarta.inject;version="[2.0.0,3.0.0)", - org.eclipse.e4.core.services.events, - org.osgi.service.event;version="[1.3.0,2.0.0)" Automatic-Module-Name: org.eclipse.e4.ui.swt.win32 diff --git a/bundles/org.eclipse.e4.ui.swt.win32/fragment.xml b/bundles/org.eclipse.e4.ui.swt.win32/fragment.xml index b4938fdbeab..79151e0318f 100644 --- a/bundles/org.eclipse.e4.ui.swt.win32/fragment.xml +++ b/bundles/org.eclipse.e4.ui.swt.win32/fragment.xml @@ -1,13 +1,5 @@ - - - - diff --git a/bundles/org.eclipse.e4.ui.swt.win32/src/org/eclipse/e4/ui/swt/internal/win32/DarkThemeProcessor.java b/bundles/org.eclipse.e4.ui.swt.win32/src/org/eclipse/e4/ui/swt/internal/win32/DarkThemeProcessor.java deleted file mode 100644 index 18c6307f236..00000000000 --- a/bundles/org.eclipse.e4.ui.swt.win32/src/org/eclipse/e4/ui/swt/internal/win32/DarkThemeProcessor.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2020 vogella GmbH and others. - * - * 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: - * Lars Vogel - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.swt.internal.win32; - -import org.eclipse.e4.core.services.events.IEventBroker; -import org.eclipse.e4.ui.css.swt.theme.ITheme; -import org.eclipse.e4.ui.css.swt.theme.IThemeEngine; -import org.eclipse.swt.internal.win32.OS; -import org.osgi.service.event.EventHandler; - -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; -import jakarta.inject.Inject; - -@SuppressWarnings("restriction") -public class DarkThemeProcessor { - - @Inject - IEventBroker eventBroker; - - private EventHandler eventHandler; - - @PostConstruct - public void intialize() { - - eventHandler = event -> { - if (event == null) { - return; - } - ITheme theme = (ITheme) event.getProperty("theme"); - boolean isDark = theme.getId().contains("dark"); //$NON-NLS-1$ - OS.setTheme (isDark); - }; - // using the IEventBroker explicitly because the @EventTopic annotation - // is unpredictable with processors within the debugger - eventBroker.subscribe(IThemeEngine.Events.THEME_CHANGED, eventHandler); - } - - @PreDestroy - public void cleanUp() { - eventBroker.unsubscribe(eventHandler); - } - -} diff --git a/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/META-INF/MANIFEST.MF index 59a4071cc79..e9ca00ec3bc 100644 --- a/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/META-INF/MANIFEST.MF @@ -6,7 +6,6 @@ Bundle-Version: 0.14.600.qualifier Bundle-Name: %fragmentName Bundle-Vendor: %providerName Fragment-Host: org.eclipse.e4.ui.workbench.renderers.swt;bundle-version="[0.10.0,1.0.0)" -Require-Bundle: org.eclipse.e4.ui.css.swt.theme Bundle-Localization: fragment-cocoa Bundle-RequiredExecutionEnvironment: JavaSE-17 Export-Package: org.eclipse.e4.ui.workbench.renderers.swt.cocoa;x-internal:=true diff --git a/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/fragment.xml b/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/fragment.xml index f0e0c11c8ce..a2ab57158c1 100644 --- a/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/fragment.xml +++ b/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/fragment.xml @@ -11,15 +11,5 @@ class="org.eclipse.e4.ui.workbench.renderers.swt.cocoa.CocoaUIProcessor"> - - - - diff --git a/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/src/org/eclipse/e4/ui/swt/internal/cocoa/CocoaDarkThemeProcessor.java b/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/src/org/eclipse/e4/ui/swt/internal/cocoa/CocoaDarkThemeProcessor.java deleted file mode 100644 index 49dc265bd2d..00000000000 --- a/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/src/org/eclipse/e4/ui/swt/internal/cocoa/CocoaDarkThemeProcessor.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2019 IBM Corporation and others. - * - * 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: - * Lakshmi Shanmugam - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.swt.internal.cocoa; - -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; -import jakarta.inject.Inject; -import org.eclipse.e4.core.services.events.IEventBroker; -import org.eclipse.e4.ui.css.swt.theme.ITheme; -import org.eclipse.e4.ui.css.swt.theme.IThemeEngine; -import org.eclipse.swt.internal.cocoa.OS; -import org.eclipse.swt.widgets.Display; -import org.osgi.service.event.EventHandler; - -@SuppressWarnings("restriction") -public class CocoaDarkThemeProcessor { - - @Inject - IEventBroker eventBroker; - - private EventHandler eventHandler; - - @PostConstruct - public void intialize() { - - eventHandler = event -> { - if (event == null) { - return; - } - ITheme theme = (ITheme) event.getProperty("theme"); //$NON-NLS-1$ - final boolean isDark = theme.getId().contains("dark"); //$NON-NLS-1$ - Display display = (Display) event.getProperty(IThemeEngine.Events.DEVICE); - - // not using UISynchronize as this is specific to SWT/Mac - // scenarios - display.asyncExec(() -> OS.setTheme(isDark)); - }; - // using the IEventBroker explicitly because the @EventTopic annotation - // is unpredictable with processors within the debugger - eventBroker.subscribe(IThemeEngine.Events.THEME_CHANGED, eventHandler); - } - - /** - * Unsubscribes the {@code eventHandler} from the {@code eventBroker} to cleanup the resources - * - * Assumption : Both {@code eventHandler} and {@code eventBroker} are initialized and non null - */ - @PreDestroy - public void cleanUp() { - eventBroker.unsubscribe(eventHandler); - } - -} diff --git a/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEApplication.java b/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEApplication.java index a4bc366a90f..41e033e06f0 100644 --- a/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEApplication.java +++ b/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEApplication.java @@ -36,12 +36,15 @@ import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExecutableExtension; +import org.eclipse.core.runtime.IProduct; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.ConfigurationScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.UserScope; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; import org.eclipse.jface.dialogs.IDialogConstants; @@ -94,6 +97,7 @@ public class IDEApplication implements IApplication, IExecutableExtension { private static final String USER_NAME = "user.name"; //$NON-NLS-1$ + // Use the branding plug-in of the platform feature since this is most likely // to change on an update of the IDE. private static final String WORKSPACE_CHECK_REFERENCE_BUNDLE_NAME = "org.eclipse.platform"; //$NON-NLS-1$ @@ -145,6 +149,9 @@ public Object start(IApplicationContext appContext) throws Exception { Job.getJobManager().suspend(); Display display = createDisplay(); + + initializeDefaultTheme(display); + // processor must be created before we start event loop DelayedEventsProcessor processor = new DelayedEventsProcessor(display); @@ -586,7 +593,6 @@ protected Shell getParentShell() { // in the task manager of the OS return null; } - }.prompt(force); } @@ -852,6 +858,28 @@ protected static Version toMajorMinorVersion(Version version) { return new Version(version.getMajor(), version.getMinor(), 0); } + protected void initializeDefaultTheme(Display display) { + IEclipsePreferences themeNode = UserScope.INSTANCE + .getNode("org.eclipse.e4.ui.css.swt.theme"); //$NON-NLS-1$ + String productOrAppId = getProductOrApplicationId(); + String defaultThemeId = null; + if (productOrAppId != null) { + defaultThemeId = themeNode.node(productOrAppId).get("themeid", null); //$NON-NLS-1$ + } + boolean isDark = defaultThemeId != null && defaultThemeId.contains("dark"); //$NON-NLS-1$ + if (isDark) { + display.setDarkThemePreferred(true); + } + } + + private static String getProductOrApplicationId() { + IProduct product = Platform.getProduct(); + if (product != null) { + return product.getId(); + } + return System.getProperty("eclipse.application"); //$NON-NLS-1$ + } + @Override public void stop() { final IWorkbench workbench = PlatformUI.getWorkbench(); @@ -865,4 +893,4 @@ public void stop() { } }); } -} \ No newline at end of file +} From 97999830a8bc2bd2afd5391015c46701bad8e9b3 Mon Sep 17 00:00:00 2001 From: Eclipse Platform Bot Date: Wed, 8 Apr 2026 10:31:54 +0000 Subject: [PATCH 2/2] Version bump(s) for 4.40 stream --- bundles/org.eclipse.e4.ui.swt.gtk/META-INF/MANIFEST.MF | 2 +- bundles/org.eclipse.e4.ui.swt.win32/META-INF/MANIFEST.MF | 2 +- .../META-INF/MANIFEST.MF | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.eclipse.e4.ui.swt.gtk/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.swt.gtk/META-INF/MANIFEST.MF index e6e792c72f2..68fd639f3ad 100644 --- a/bundles/org.eclipse.e4.ui.swt.gtk/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.swt.gtk/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %fragmentName Bundle-SymbolicName: org.eclipse.e4.ui.swt.gtk;singleton:=true -Bundle-Version: 1.2.200.qualifier +Bundle-Version: 1.2.300.qualifier Fragment-Host: org.eclipse.e4.ui.css.swt.theme;bundle-version="0.10.0" Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-Localization: fragment-gtk diff --git a/bundles/org.eclipse.e4.ui.swt.win32/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.swt.win32/META-INF/MANIFEST.MF index 9795fd9215a..2a570b89b43 100644 --- a/bundles/org.eclipse.e4.ui.swt.win32/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.swt.win32/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %fragmentName Bundle-SymbolicName: org.eclipse.e4.ui.swt.win32;singleton:=true -Bundle-Version: 1.2.300.qualifier +Bundle-Version: 1.2.400.qualifier Fragment-Host: org.eclipse.e4.ui.css.swt.theme;bundle-version="0.10.0" Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-Localization: fragment-win32 diff --git a/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/META-INF/MANIFEST.MF index e9ca00ec3bc..7c8b35b8389 100644 --- a/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.workbench.renderers.swt.cocoa/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Eclipse-PlatformFilter: (osgi.ws=cocoa) Bundle-SymbolicName: org.eclipse.e4.ui.workbench.renderers.swt.cocoa;singleton:=true -Bundle-Version: 0.14.600.qualifier +Bundle-Version: 0.14.700.qualifier Bundle-Name: %fragmentName Bundle-Vendor: %providerName Fragment-Host: org.eclipse.e4.ui.workbench.renderers.swt;bundle-version="[0.10.0,1.0.0)"