[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openjdk-openjfx-dev
Subject:    Re: RFR: 8267546: Add CSS themes as a first-class concept [v14]
From:       Michael =?UTF-8?B?U3RyYXXDnw==?= <mstrauss () openjdk ! org>
Date:       2022-12-26 3:58:30
Message-ID: BJI93m8sucddXLR-owK61W-QGoaGC-z6fYbeQjsuy7o=.b0d0ec15-1230-489a-a6d8-aecc79cf5685 () github ! com
[Download RAW message or body]

> 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 20, 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. PlatformPreferences
> `javafx.application.PlatformPreferences` 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 `Map` 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 PlatformPreferences} implements {@link Map} 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 PlatformPreferences extends Map<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);
> 
> void addListener(PlatformPreferencesListener listener);
> void removeListener(PlatformPreferencesListener listener);
> }
> 
> An instance of `PlatformPreferences` can be retrieved via \
> `Platform.getPreferences()`. 
> ### 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-since tags

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

Changes:
  - all: https://git.openjdk.org/jfx/pull/511/files
  - new: https://git.openjdk.org/jfx/pull/511/files/2dedeea7..444d4c0d

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

  Stats: 3 lines in 3 files changed: 0 ins; 0 del; 3 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


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic