> This PR adds style themes as a first-class concept to OpenJFX. A style theme 
> is a collection of stylesheets and the logic that governs them. Style themes 
> can respond to OS notifications and update their stylesheets dynamically. 
> This PR also re-implements Caspian and Modena as style themes.
> 
> ### New APIs in `javafx.graphics`
> The new theming-related APIs in `javafx.graphics` provide a basic framework 
> to support application-wide style themes. Higher-level theming concepts (for 
> example, "dark mode" detection or accent coloring) are not a part of this 
> basic framework, because any API invented here might soon be out of date. 
> Implementations can build on top of this framework to add useful higher-level 
> features.
> #### 1. StyleTheme
> A style theme is an implementation of the `javafx.css.StyleTheme` interface:
> 
> /**
>  * {@code StyleTheme} is a collection of stylesheets that specify the 
> appearance of UI controls and other
>  * nodes in the application. Like a user-agent stylesheet, a {@code 
> StyleTheme} is implicitly used by all
>  * JavaFX nodes in the scene graph.
>  * <p>
>  * The list of stylesheets that comprise a {@code StyleTheme} can be modified 
> while the application is running,
>  * enabling applications to create dynamic themes that respond to changing 
> user preferences.
>  * <p>
>  * In the CSS subsystem, stylesheets that comprise a {@code StyleTheme} are 
> classified as
>  * {@link StyleOrigin#USER_AGENT} stylesheets, but have a higher precedence 
> in the CSS cascade
>  * than a stylesheet referenced by {@link 
> Application#userAgentStylesheetProperty()}.
>  */
> public interface StyleTheme {
>     /**
>      * Gets the list of stylesheet URLs that comprise this {@code StyleTheme}.
>      * <p>
>      * If the list of stylesheets that comprise this {@code StyleTheme} is 
> changed at runtime, this
>      * method must return an {@link ObservableList} to allow the CSS 
> subsystem to subscribe to list
>      * change notifications.
>      * 
>      * @implSpec Implementations of this method that return an {@link 
> ObservableList} must emit all
>      *           change notifications on the JavaFX application thread.
>      *
>      * @implNote Implementations of this method that return an {@link 
> ObservableList} are encouraged
>      *           to minimize the number of subsequent list change 
> notifications that are fired by the
>      *           list, as each change notification causes the CSS subsystem 
> to re-apply the referenced
>      *           stylesheets.
>      */
>     List<String> getStylesheets();
> }
> 
> 
> A new `styleTheme` property is added to `javafx.application.Application`, and 
> `userAgentStylesheet` is promoted to a JavaFX property (currently, this is 
> just a getter/setter pair):
> 
> public class Application {
>     ...
>     /**
>      * Specifies the user-agent stylesheet of the application.
>      * <p>
>      * A user-agent stylesheet is a global stylesheet that can be specified 
> in addition to a
>      * {@link StyleTheme} and that is implicitly used by all JavaFX nodes in 
> the scene graph.
>      * It can be used to provide default styling for UI controls and other 
> nodes.
>      * A user-agent stylesheets has the lowest precedence in the CSS cascade.
>      * <p>
>      * Before JavaFX 21, built-in themes were selectable using the special 
> user-agent stylesheet constants
>      * {@link #STYLESHEET_CASPIAN} and {@link #STYLESHEET_MODENA}. For 
> backwards compatibility, the meaning
>      * of these special constants is retained: setting the user-agent 
> stylesheet to either {@code STYLESHEET_CASPIAN}
>      * or {@code STYLESHEET_MODENA} will also set the value of the {@link 
> #styleThemeProperty() styleTheme}
>      * property to a new instance of the corresponding theme class.
>      * <p>
>      * Note: this property can be modified on any thread, but it is not 
> thread-safe and must
>      *       not be concurrently modified with {@link #styleThemeProperty() 
> styleTheme}.
>      */
>     public static StringProperty userAgentStylesheetProperty();
>     public static String getUserAgentStylesheet();
>     public static void setUserAgentStylesheet(String url);
> 
>     /**
>      * Specifies the {@link StyleTheme} of the application.
>      * <p>
>      * {@code StyleTheme} is a collection of stylesheets that define the 
> appearance of the application.
>      * Like a user-agent stylesheet, a {@code StyleTheme} is implicitly used 
> by all JavaFX nodes in the
>      * scene graph.
>      * <p>
>      * Stylesheets that comprise a {@code StyleTheme} have a higher 
> precedence in the CSS cascade than a
>      * stylesheet referenced by the {@link #userAgentStylesheetProperty() 
> userAgentStylesheet} property.
>      * <p>
>      * Note: this property can be modified on any thread, but it is not 
> thread-safe and must not be
>      *       concurrently modified with {@link #userAgentStylesheetProperty() 
> userAgentStylesheet}.
>      */
>     public static ObjectProperty<StyleTheme> styleThemeProperty();
>     public static StyleTheme getStyleTheme();
>     public static void setStyleTheme(StyleTheme theme);
>     ...
> }
> 
> 
> `styleTheme` and `userAgentStylesheet` are correlated to preserve backwards 
> compatibility: setting `userAgentStylesheet` to the magic values "CASPIAN" or 
> "MODENA" will implicitly set `styleTheme` to a new instance of the 
> `CaspianTheme` or `ModenaTheme` class. Aside from these magic values, 
> `userAgentStylesheet` can be set independently from `styleTheme`. In the CSS 
> cascade, `userAgentStylesheet` has a lower precedence than `styleTheme`.
> 
> #### 2. Preferences
> `javafx.application.Platform.Preferences` can be used to query UI-related 
> information about the current platform to allow theme implementations to 
> adapt to the operating system. The interface extends `ObservableMap` and adds 
> several useful methods, as well as the option to register a listener for 
> change notifications:
> 
> /**
>  * Contains UI preferences of the current platform.
>  * <p>
>  * {@code Preferences} extends {@link ObservableMap} to expose platform 
> preferences as key-value pairs.
>  * For convenience, {@link #getString}, {@link #getBoolean} and {@link 
> #getColor} are provided as typed
>  * alternatives to the untyped {@link #get} method.
>  * <p>
>  * The preferences that are reported by the platform may be dependent on the 
> operating system version.
>  * Applications should always test whether a preference is available, or use 
> the {@link #getString(String, String)},
>  * {@link #getBoolean(String, boolean)} or {@link #getColor(String, Color)} 
> overloads that accept a fallback
>  * value if the preference is not available.
>  */
> public interface Preferences extends ObservableMap<String, Object> {
>     String getString(String key);
>     String getString(String key, String fallbackValue);
> 
>     Boolean getBoolean(String key);
>     boolean getBoolean(String key, boolean fallbackValue);
> 
>     Color getColor(String key);
>     Color getColor(String key, Color fallbackValue);
> }
> 
> An instance of `Preferences` can be retrieved via `Platform.getPreferences()`.
> 
> Here's a list of the preferences available for Windows, as reported by the 
> [SystemParametersInfo](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow),
>  
> [GetSysColor](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsyscolor)
>  and 
> [Windows.UI.ViewManagement.UISettings.GetColorValue](https://learn.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.uisettings.getcolorvalue)
>  APIs. Deprecated colors are not included.
> | Windows preferences                                  | Type    |
> |--------------------------------------|---------|
> | Windows.SPI.HighContrast             | Boolean |
> | Windows.SPI.HighContrastColorScheme  | String  |
> | Windows.SysColor.COLOR_3DFACE        | Color   |
> | Windows.SysColor.COLOR_BTNTEXT       | Color   |
> | Windows.SysColor.COLOR_GRAYTEXT      | Color   |
> | Windows.SysColor.COLOR_HIGHLIGHT     | Color   |
> | Windows.SysColor.COLOR_HIGHLIGHTTEXT | Color   |
> | Windows.SysColor.COLOR_HOTLIGHT      | Color   |
> | Windows.SysColor.COLOR_WINDOW        | Color   |
> | Windows.SysColor.COLOR_WINDOWTEXT    | Color   |
> | Windows.UIColor.Background           | Color   |
> | Windows.UIColor.Foreground           | Color   |
> | Windows.UIColor.AccentDark3          | Color   |
> | Windows.UIColor.AccentDark2          | Color   |
> | Windows.UIColor.AccentDark1          | Color   |
> | Windows.UIColor.Accent               | Color   |
> | Windows.UIColor.AccentLight1         | Color   |
> | Windows.UIColor.AccentLight2         | Color   |
> | Windows.UIColor.AccentLight3         | Color   |
> 
> Here is a list of macOS preferences as reported by `NSColor`'s [UI Element 
> Colors](https://developer.apple.com/documentation/appkit/nscolor/ui_element_colors)
>  and [Adaptable System 
> Colors](https://developer.apple.com/documentation/appkit/nscolor/standard_colors).
>  Deprecated colors are not included.
> | macOS preferences                                                      | 
> Type    |
> |----------------------------------------------------------|---------|
> | macOS.NSColor.labelColor                                 | Color   |
> | macOS.NSColor.secondaryLabelColor                        | Color   |
> | macOS.NSColor.tertiaryLabelColor                         | Color   |
> | macOS.NSColor.quaternaryLabelColor                       | Color   |
> | macOS.NSColor.textColor                                  | Color   |
> | macOS.NSColor.placeholderTextColor                       | Color   |
> | macOS.NSColor.selectedTextColor                          | Color   |
> | macOS.NSColor.textBackgroundColor                        | Color   |
> | macOS.NSColor.selectedTextBackgroundColor                | Color   |
> | macOS.NSColor.keyboardFocusIndicatorColor                | Color   |
> | macOS.NSColor.unemphasizedSelectedTextColor              | Color   |
> | macOS.NSColor.unemphasizedSelectedTextBackgroundColor    | Color   |
> | macOS.NSColor.linkColor                                  | Color   |
> | macOS.NSColor.separatorColor                             | Color   |
> | macOS.NSColor.selectedContentBackgroundColor             | Color   |
> | macOS.NSColor.unemphasizedSelectedContentBackgroundColor | Color   |
> | macOS.NSColor.selectedMenuItemTextColor                  | Color   |
> | macOS.NSColor.gridColor                                  | Color   |
> | macOS.NSColor.headerTextColor                            | Color   |
> | macOS.NSColor.alternatingContentBackgroundColors         | Color[] |
> | macOS.NSColor.controlAccentColor                         | Color   |
> | macOS.NSColor.controlColor                               | Color   |
> | macOS.NSColor.controlBackgroundColor                     | Color   |
> | macOS.NSColor.controlTextColor                           | Color   |
> | macOS.NSColor.disabledControlTextColor                   | Color   |
> | macOS.NSColor.selectedControlColor                       | Color   |
> | macOS.NSColor.selectedControlTextColor                   | Color   |
> | macOS.NSColor.alternateSelectedControlTextColor          | Color   |
> | macOS.NSColor.currentControlTint                         | String  |
> | macOS.NSColor.windowBackgroundColor                      | Color   |
> | macOS.NSColor.windowFrameTextColor                       | Color   |
> | macOS.NSColor.underPageBackgroundColor                   | Color   |
> | macOS.NSColor.findHighlightColor                         | Color   |
> | macOS.NSColor.highlightColor                             | Color   |
> | macOS.NSColor.shadowColor                                | Color   |
> | macOS.NSColor.systemBlueColor                            | Color   |
> | macOS.NSColor.systemBrownColor                           | Color   |
> | macOS.NSColor.systemGrayColor                            | Color   |
> | macOS.NSColor.systemGreenColor                           | Color   |
> | macOS.NSColor.systemIndigoColor                          | Color   |
> | macOS.NSColor.systemOrangeColor                          | Color   |
> | macOS.NSColor.systemPinkColor                            | Color   |
> | macOS.NSColor.systemPurpleColor                          | Color   |
> | macOS.NSColor.systemRedColor                             | Color   |
> | macOS.NSColor.systemTealColor                            | Color   |
> | macOS.NSColor.systemYellowColor                          | Color   |
> 
> On Linux, GTK's theme name and [public CSS 
> colors](https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-22/gtk/theme/Adwaita/_colors-public.scss)
>  are reported:
> | Linux preferences                                                | Type    |
> |----------------------------------------------------|---------|
> | GTK.theme_name                                | String  |
> | GTK.theme_fg_color                    | Color   |
> | GTK.theme_bg_color                    | Color   |
> | GTK.theme_base_color                  | Color   |
> | GTK.theme_selected_bg_color           | Color   |
> | GTK.theme_selected_fg_color           | Color   |
> | GTK.insensitive_bg_color              | Color   |
> | GTK.insensitive_fg_color              | Color   |
> | GTK.insensitive_base_color            | Color   |
> | GTK.theme_unfocused_fg_color          | Color   |
> | GTK.theme_unfocused_bg_color          | Color   |
> | GTK.theme_unfocused_base_color        | Color   |
> | GTK.theme_unfocused_selected_bg_color | Color   |
> | GTK.theme_unfocused_selected_fg_color | Color   |
> | GTK.borders                           | Color   |
> | GTK.unfocused_borders                 | Color   |
> | GTK.warning_color                     | Color   |
> | GTK.error_color                       | Color   |
> | GTK.success_color                     | Color   |
> 
> ### Built-in themes
> The two built-in themes `CaspianTheme` and `ModenaTheme` are exposed as 
> public API in the `javafx.scene.control.theme` package. Both classes extend 
> `ThemeBase`, which is a simple `StyleTheme` implementation that allows 
> developers to easily extend the built-in themes.
> 
> ### Usage
> In its simplest form, a style theme is just a static collection of 
> stylesheets:
> 
> Application.setStyleTheme(() -> List.of("stylesheet1.css", "stylesheet2.css");
> 
> A dynamic theme can be created by returning an instance of `ObservableList`:
> 
> public class MyCustomTheme implements StyleTheme {
>     private final ObservableList<String> stylesheets =
>         FXCollections.observableArrayList("colors-light.css", "controls.css");
> 
>     @Override
>     public List<String> getStylesheets() {
>         return stylesheets;
>     }
> 
>     public void setDarkMode(boolean enabled) {
>         stylesheets.set(0, enabled ? "colors-dark.css" : "colors-light.css");
>     }
> }
> 
> `CaspianTheme` and `ModenaTheme` can be extended by prepending or appending 
> additional stylesheets:
> 
> Application.setStyleTheme(new ModenaTheme() {
>     {
>         addFirst("stylesheet1.css");
>         addLast("stylesheet2.css");
>     }
> });

Michael Strauß has updated the pull request incrementally with one additional 
commit since the last revision:

  update javadoc

-------------

Changes:
  - all: https://git.openjdk.org/jfx/pull/511/files
  - new: https://git.openjdk.org/jfx/pull/511/files/12882080..27ead34f

Webrevs:
 - full: https://webrevs.openjdk.org/?repo=jfx&pr=511&range=19
 - incr: https://webrevs.openjdk.org/?repo=jfx&pr=511&range=18-19

  Stats: 2 lines in 1 file changed: 0 ins; 0 del; 2 mod
  Patch: https://git.openjdk.org/jfx/pull/511.diff
  Fetch: git fetch https://git.openjdk.org/jfx pull/511/head:pull/511

PR: https://git.openjdk.org/jfx/pull/511

Reply via email to