diff --git a/CLAUDE.md b/CLAUDE.md index d4f41a0..567f8f5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,9 +21,9 @@ The update site lands in `com.vogella.examples.updatesite/target/repository`. - Pomless Tycho 5 build: `.mvn/extensions.xml` loads `tycho-build`, so bundles, the feature and the update site have no `pom.xml` of their own and must not get one. - The root `pom.xml` holds the module list, the Java release and the target environments (linux, win32, macosx). -- `target-platform/target-platform.target` resolves everything from the Eclipse 2026-09 release repository; a new dependency outside the listed features must be added there as a unit. +- `target-platform/target-platform.target` resolves everything from the Eclipse 4.42 I-build repository, because `com.vogella.rcp.svg.currentcolor` needs `SVGFileFormat.setCurrentColor`, which is newer than the 2026-09 release; switch it to the 2026-12 release repository once that is out. A new dependency outside the listed features must be added there as a unit. - `com.vogella.examples.feature` includes every example bundle, and `com.vogella.examples.updatesite/category.xml` publishes that feature. -- The e4 examples are PDE template applications (`Application.e4xmi`, handlers, parts) whose `plugin.xml` product extension lets them be launched from the IDE; only `com.vogella.css.fonts` also has a `.product` file, which launches it with just the bundles it needs. +- The e4 examples are PDE template applications (`Application.e4xmi`, handlers, parts) whose `plugin.xml` product extension lets them be launched from the IDE; only `com.vogella.css.fonts` and `com.vogella.rcp.svg.currentcolor` also have a `.product` file, which launches them with just the bundles they need. - `com.vogella.rcp.perspective.plugin` and `com.vogella.rcp.perspective.addpart` contribute model fragments into `com.vogella.rcp.jface.translation`, so its element ids must stay stable. - `org.eclipse.jface.nl1` is a fragment of `org.eclipse.jface` that supplies translated messages for the translation example. - `com.vogella.css.fonts` targets Windows (it reads the win32 `LOGFONT` by reflection) but compiles on every platform. diff --git a/README.md b/README.md index 4cea2e8..6b6aed2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # vogella Eclipse code examples Example projects for Eclipse RCP and plug-in development from the [vogella.com](https://www.vogella.com) website. -The bundles are built with Tycho (pomless) against the Eclipse 2026-09 release and published as one p2 update site. +The bundles are built with Tycho (pomless) against the Eclipse 4.42 integration builds and published as one p2 update site. +The integration builds are needed for the SWT API used by `com.vogella.rcp.svg.currentcolor`; the target moves to the 2026-12 release once it is available. ## Examples @@ -19,6 +20,7 @@ The bundles are built with Tycho (pomless) against the Eclipse 2026-09 release a | com.vogella.rcp.jface.translation | Translating JFace wizards at runtime | | com.vogella.rcp.perspective.addpart | Adding a part to a perspective with a model fragment | | com.vogella.rcp.perspective.plugin | Contributing a perspective from a plug-in | +| com.vogella.rcp.svg.currentcolor | Setting the `currentColor` of SVG icons with `SVGFileFormat.setCurrentColor` from a `-currentColor` program argument or a login dialog; start it from `svg-currentcolor.product` | | com.vogella.swt.fonts.roboto | Loading a custom font (Roboto) in SWT | | com.vogella.swt.widgets | Custom SWT widgets | | org.eclipse.jface.nl1 | Fragment with JFace translations | diff --git a/com.vogella.examples.feature/feature.xml b/com.vogella.examples.feature/feature.xml index 22c2236..e083120 100644 --- a/com.vogella.examples.feature/feature.xml +++ b/com.vogella.examples.feature/feature.xml @@ -28,6 +28,7 @@ + diff --git a/com.vogella.rcp.svg.currentcolor/.classpath b/com.vogella.rcp.svg.currentcolor/.classpath new file mode 100644 index 0000000..7a42e53 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/com.vogella.rcp.svg.currentcolor/.project b/com.vogella.rcp.svg.currentcolor/.project new file mode 100644 index 0000000..b692bba --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/.project @@ -0,0 +1,28 @@ + + + com.vogella.rcp.svg.currentcolor + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/com.vogella.rcp.svg.currentcolor/.settings/org.eclipse.core.resources.prefs b/com.vogella.rcp.svg.currentcolor/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/com.vogella.rcp.svg.currentcolor/.settings/org.eclipse.jdt.core.prefs b/com.vogella.rcp.svg.currentcolor/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..c540a97 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=25 +org.eclipse.jdt.core.compiler.compliance=25 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=25 diff --git a/com.vogella.rcp.svg.currentcolor/Application.e4xmi b/com.vogella.rcp.svg.currentcolor/Application.e4xmi new file mode 100644 index 0000000..d1e06c3 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/Application.e4xmi @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.vogella.rcp.svg.currentcolor/META-INF/MANIFEST.MF b/com.vogella.rcp.svg.currentcolor/META-INF/MANIFEST.MF new file mode 100644 index 0000000..01542ba --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: SVG currentColor +Bundle-SymbolicName: com.vogella.rcp.svg.currentcolor;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: VOGELLA +Require-Bundle: org.eclipse.swt, + org.eclipse.e4.ui.model.workbench, + org.eclipse.jface, + org.eclipse.e4.ui.services, + org.eclipse.e4.ui.workbench, + org.eclipse.e4.core.di, + org.eclipse.e4.ui.di, + org.eclipse.e4.core.contexts, + org.eclipse.core.runtime +Bundle-RequiredExecutionEnvironment: JavaSE-25 +Automatic-Module-Name: com.vogella.rcp.svg.currentcolor +Import-Package: jakarta.annotation;version="[2.1.0,4.0.0)", + jakarta.inject;version="[2.0.0,3.0.0)" +Require-Capability: eclipse.swt;filter:="(image.format=svg)" diff --git a/com.vogella.rcp.svg.currentcolor/build.properties b/com.vogella.rcp.svg.currentcolor/build.properties new file mode 100644 index 0000000..85891d7 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/build.properties @@ -0,0 +1,7 @@ +source.. = src/ +output.. = bin/ +bin.includes = plugin.xml,\ + META-INF/,\ + .,\ + icons/,\ + Application.e4xmi diff --git a/com.vogella.rcp.svg.currentcolor/icons/exit.svg b/com.vogella.rcp.svg.currentcolor/icons/exit.svg new file mode 100644 index 0000000..0583529 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/icons/exit.svg @@ -0,0 +1,3 @@ + + + diff --git a/com.vogella.rcp.svg.currentcolor/icons/folder.svg b/com.vogella.rcp.svg.currentcolor/icons/folder.svg new file mode 100644 index 0000000..2c3a109 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/icons/folder.svg @@ -0,0 +1,3 @@ + + + diff --git a/com.vogella.rcp.svg.currentcolor/icons/home.svg b/com.vogella.rcp.svg.currentcolor/icons/home.svg new file mode 100644 index 0000000..03ad6d3 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/icons/home.svg @@ -0,0 +1,3 @@ + + + diff --git a/com.vogella.rcp.svg.currentcolor/icons/info.svg b/com.vogella.rcp.svg.currentcolor/icons/info.svg new file mode 100644 index 0000000..86db670 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/icons/info.svg @@ -0,0 +1,3 @@ + + + diff --git a/com.vogella.rcp.svg.currentcolor/icons/save.svg b/com.vogella.rcp.svg.currentcolor/icons/save.svg new file mode 100644 index 0000000..069d10a --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/icons/save.svg @@ -0,0 +1,3 @@ + + + diff --git a/com.vogella.rcp.svg.currentcolor/icons/settings.svg b/com.vogella.rcp.svg.currentcolor/icons/settings.svg new file mode 100644 index 0000000..0b209ab --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/icons/settings.svg @@ -0,0 +1,4 @@ + + + + diff --git a/com.vogella.rcp.svg.currentcolor/icons/star.svg b/com.vogella.rcp.svg.currentcolor/icons/star.svg new file mode 100644 index 0000000..eddf7ee --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/icons/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/com.vogella.rcp.svg.currentcolor/plugin.xml b/com.vogella.rcp.svg.currentcolor/plugin.xml new file mode 100644 index 0000000..5bab4a3 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/plugin.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/CurrentColor.java b/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/CurrentColor.java new file mode 100644 index 0000000..2c56ae2 --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/CurrentColor.java @@ -0,0 +1,28 @@ +package com.vogella.rcp.svg.currentcolor; + +import java.util.Optional; + +import org.eclipse.swt.graphics.RGB; + +/** + * Converts between {@link RGB} and the {@code RRGGBB} notation, with an optional + * leading {@code #}, that the {@code -currentColor} program argument and SWT's + * {@code swt.svg.currentColor} system property use. + */ +public final class CurrentColor { + + private CurrentColor() { + } + + public static Optional parse(String value) { + if (value == null || !value.trim().matches("#?[0-9A-Fa-f]{6}")) { + return Optional.empty(); + } + int rgb = Integer.parseInt(value.trim().replaceFirst("^#", ""), 16); + return Optional.of(new RGB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF)); + } + + public static String toHex(RGB rgb) { + return "#%02X%02X%02X".formatted(rgb.red, rgb.green, rgb.blue); + } +} diff --git a/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/Icons.java b/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/Icons.java new file mode 100644 index 0000000..7f2e5dc --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/Icons.java @@ -0,0 +1,32 @@ +package com.vogella.rcp.svg.currentcolor; + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +/** + * The SVG icons of this bundle, which all use {@code currentColor}. + */ +public final class Icons { + + public static final String[] NAMES = { "home", "folder", "save", "star", "settings", "info", "exit" }; + + private Icons() { + } + + /** + * Creates the icon with the {@code currentColor} that is set now; the caller + * disposes it. + */ + public static Image create(Display display, String name) { + try (InputStream in = Icons.class.getResourceAsStream("/icons/" + name + ".svg")) { + return in != null ? new Image(display, in) : null; + } catch (IOException e) { + ILog.of(Icons.class).error("Could not load icon " + name, e); + return null; + } + } +} diff --git a/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/LifeCycle.java b/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/LifeCycle.java new file mode 100644 index 0000000..e3c7caf --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/LifeCycle.java @@ -0,0 +1,66 @@ +package com.vogella.rcp.svg.currentcolor; + +import java.util.Optional; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.Platform; +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.internal.image.SVGFileFormat; + +/** + * Sets the color SVG images use for {@code currentColor} before the workbench + * window is rendered. + *

+ * The color comes from the login dialog, which preselects the color of the + * {@code -currentColor RRGGBB} program argument if one is given, otherwise + * SWT's current value (black, or the {@code -Dswt.svg.currentColor=RRGGBB} + * system property). + *

+ *

+ * The Eclipse CSS property {@code swt-svg-current-color} is not available yet, + * so the color is set with the SWT method + * {@link SVGFileFormat#setCurrentColor(RGB)}. The color is global and is used + * when an image is rasterized, so it must be set before the first SVG icon is + * created. + *

+ */ +@SuppressWarnings("restriction") +public class LifeCycle { + + static final String CURRENT_COLOR_ARGUMENT = "-currentColor"; + + /** Context key of the name entered in the login dialog. */ + public static final String USER = "com.vogella.rcp.svg.currentcolor.user"; + + @PostContextCreate + void login(IEclipseContext workbenchContext) { + RGB initialColor = colorFromArguments().orElseGet(SVGFileFormat::getCurrentColor); + + LoginDialog dialog = new LoginDialog(null, initialColor); + if (dialog.open() != Window.OK) { + // no workbench window exists yet, so ending the process is the simplest way to cancel the start + System.exit(0); + } + + SVGFileFormat.setCurrentColor(dialog.getCurrentColor()); + workbenchContext.set(USER, dialog.getUser()); + } + + private Optional colorFromArguments() { + String[] args = Platform.getApplicationArgs(); + for (int i = 0; i < args.length - 1; i++) { + if (CURRENT_COLOR_ARGUMENT.equalsIgnoreCase(args[i])) { + Optional color = CurrentColor.parse(args[i + 1]); + if (color.isEmpty()) { + ILog.of(getClass()).warn("Ignoring " + CURRENT_COLOR_ARGUMENT + " " + args[i + 1] + + ", expected a color like 1E88E5"); + } + return color; + } + } + return Optional.empty(); + } +} diff --git a/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/LoginDialog.java b/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/LoginDialog.java new file mode 100644 index 0000000..902fe2c --- /dev/null +++ b/com.vogella.rcp.svg.currentcolor/src/com/vogella/rcp/svg/currentcolor/LoginDialog.java @@ -0,0 +1,198 @@ +package com.vogella.rcp.svg.currentcolor; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.internal.image.SVGFileFormat; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.ColorDialog; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * Login screen which also lets the user pick the color of the {@code currentColor} + * SVG icons. The preview rasterizes the icons again whenever the color changes. + */ +@SuppressWarnings("restriction") +public class LoginDialog extends TitleAreaDialog { + + private record NamedColor(String name, RGB rgb) { + } + + private static final List PRESETS = List.of( + new NamedColor("Black", new RGB(0, 0, 0)), + new NamedColor("Light gray (dark theme)", new RGB(0xCC, 0xCC, 0xCC)), + new NamedColor("Blue", new RGB(0x1E, 0x88, 0xE5)), + new NamedColor("Green", new RGB(0x2E, 0x7D, 0x32)), + new NamedColor("Orange", new RGB(0xEF, 0x6C, 0x00))); + + private final List colors = new ArrayList<>(PRESETS); + private final List previewImages = new ArrayList<>(); + private final List