Make themes a first-class concept in JavaFX
Michael Strauß
michaelstrau2 at gmail.com
Fri May 21 04:36:57 UTC 2021
Currently, the two themes shipped with JavaFX (Caspian and Modena) are
implemented as a set of stylesheets and some internal logic, mostly in
`PlatformImpl`.
Much of this logic deals with optional features or platform-specific
theme modifications. Another piece of logic deals with accessibility
themes: on Windows platforms, both Caspian and Modena have
high-contrast stylesheets that are added or removed in response to OS
theme changes.
Apart from the fact that high-contrast detection only works on English
locales (and there's no simple fix, see JDK-8185447), a major downside
of this implementation is that it special-cases the built-in themes
and doesn't offer enough extension points to make it easy for
developers to create new rich themes.
I think this can be very much improved by making themes a first-class
citizen in JavaFX, and removing the special handling of the two
built-in themes. Here's my draft PR of this feature:
https://github.com/openjdk/jfx/pull/511
This PR adds a new interface in the javafx.application package:
public interface Theme {
ObservableList<String> getStylesheets();
void platformThemeChanged(Map<String, String> properties);
}
An implementation of this interface populates the list returned by
`Theme.getStylesheets()` with its collection of stylesheets.
Optionally, it can listen to the platform theme properties reported by
`Theme.platformThemeChanged(...)` and modify its list of stylesheets
accordingly.
Platform properties are platform-specific key-value pairs, and theme
implementations are responsible for interpreting the values. For
example, on the Windows platform, a theme implementation might listen
to the "Windows.SPI_HighContrastOn" property in order to detect
whether or not a high-contrast stylesheet should be used. The `Theme`
class documents all currently supported platform properties.
A theme is activated by setting it as the user-agent stylesheet of the
application, where "theme:" is a custom URI scheme:
Application.setUserAgentStylesheet("theme:com.example.CustomTheme");
The existing constants `Application.STYLESHEET_CASPIAN` and
`Application.STYLESHEET_MODENA` are simply redefined to their new
`Theme` implementations
("theme:com.sun.javafx.application.theme.Caspian" and
"theme:com.sun.javafx.application.theme.Modena").
Because theme-specific code is now located in its corresponding
`Theme` class, I've been able to clean up `PlatformImpl` so that it
doesn't special-case the built-in themes anymore.
Going forward: If we add support for loading stylesheets from
data-URIs (similar to https://github.com/openjdk/jfx/pull/508), we can
then fix the high-contrast themes by reading the system colors
reported back by Windows, and creating an in-memory stylesheet for
these colors within the `Modena` implementation.
This feature will also allow developers to create themes that
dynamically adjust to OS-level accent colors or dark mode settings.
For example, a `Theme` implementation can detect dark mode on Windows
10 by listening to the
"Windows.UI.ViewManagement.UISettings.ColorValue_Background" and
"Windows.UI.ViewManagement.UISettings.ColorValue_Foreground"
properties.
I'm looking forward to any opinions on this.
More information about the openjfx-dev
mailing list