On Mon, 9 Jan 2023 18:33:40 GMT, Michael Strauß <mstra...@openjdk.org> wrote:

>> 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 two additional 
> commits since the last revision:
> 
>  - Changed GTK preference keys
>  - Platform.Preferences implements ObservableMap

Hi all,

I’ve seen in the mailing list a request for commenting on this PR and as a 
long-time theme developer (JMetro and other themes) I'd thought I'd give my 2 
cents.

I think it’s a good idea to add the concept of a theme to the JavaFX API! So, 
thanks for this.

1 - Reading through the javadocs in this PR and the description, I think it’s 
not clear whether the stylesheets of a StyleTheme will be used as user agent 
stylesheets or as author stylesheets. It says that StyleThemes have higher 
precedence than a user agent stylesheet so I suppose they are going to be 
author stylesheets (?), but there’s no point in the Javadoc where that is 
explicitly said. If that’s not the case, then I think the opposite should then 
be explicitly written. I.e. that it will have higher precedence than a user 
agent stylesheet but it’s not an author stylesheet.

2 – I think the ability to specify in the StyleTheme whether it is a user agent 
stylesheet, or an author stylesheet could be of interest. Most of the themes I 
know are composed of author stylesheets (they use the getStylesheets() API in 
Scene) so migration to using the StyleTheme API would be easier if one could 
specify this. Conversely specifying that the StyleTheme is a user agent 
stylesheet will also be very useful in the cases where we’re creating a theme 
but don’t want it to override styles specified through code, etc.

3 – I would really love for JavaFX to have first class support for dark and 
light modes, and I think this would be the ideal place for it.  One of the 
problems with how things are right now is that if you create a dark theme with 
this API or the previous API (using stylesheets) the frames of windows (main 
windows and dialogs) will still show with a light theme (I see this on Windows, 
haven’t tested on Mac but I suppose it will be the same). 
So as it is, you can’t fully create a dark theme in JavaFX. The only way would 
be to call on native code to change the frame of windows (by making a request 
for the native window to change its appearance to dark mode) which isn’t 
trivial and would have to be done for the various operating systems and it’s 
subject to break. 
The other way would be to create your own frames of the windows which I think 
would be even worse. You’d have to create the frame buttons yourself and all 
other decorations. If they don’t visually exactly match the ones from the 
native platform they’re going to look off. You’d also have to do this for all 
operating systems and for their various versions (various versions of the same 
OS might have different frame decorations, e.g. win10 vs win11). 
Given that dark and light theme concepts are pervasive across all major 
operating systems (Windows, Mac, iOS, Android, etc) and it’s a concept that has 
lasted for years and continues to exist, I think it would be of value to fully 
support this.

Thanks

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

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

Reply via email to