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 779d1ad19418..137edc5df3c8 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 8ddcf3d802ef..5c7e47244597 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 c8a810050255..e6e792c72f22 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 21607e4994db..79151e0318fd 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 6152674e7779..000000000000 --- 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 187d89e9d461..9795fd9215a5 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 b4938fdbeab5..79151e0318fd 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 18c6307f2369..000000000000 --- 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 59a4071cc79f..e9ca00ec3bc7 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 f0e0c11c8ce1..a2ab57158c14 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 49dc265bd2d5..000000000000 --- 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 a4bc366a90fa..41e033e06f04 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 e6e792c72f22..68fd639f3ad8 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 9795fd9215a5..2a570b89b434 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 e9ca00ec3bc7..7c8b35b8389a 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)"