RFR: 8301302: Platform preferences API [v3]
Michael Strauß
mstrauss at openjdk.org
Mon Jul 24 17:45:37 UTC 2023
On Sun, 9 Apr 2023 20:23:55 GMT, John Hendrikx <jhendrikx at openjdk.org> wrote:
> With the preferences Map, this could work similar perhaps; set a key to whatever you want with put, and restore it to its original value by setting it to null.
That's how the current API works, with a little bit of added complexity:
interface Preferences {
...
/**
* Overrides the value of the {@link #appearanceProperty() appearance} property.
* <p>
* Specifying {@code null} clears the override, which restores the value of the
* {@code appearance} property to the platform-provided value.
* <p>
* Calling this method does not update the {@code appearance} property instantaneously;
* instead, the property is only updated after calling {@link #commit()}, or after the
* occurrence of an operating system event that causes the {@code appearance} property
* to be recomputed.
*
* @param appearance the platform appearance override, or {@code null} to clear the override
*/
void setAppearance(Appearance appearance);
...
/**
* Overrides a key-value mapping.
* <p>
* If a platform-provided mapping for the key already exists, calling this method overrides
* the value that is mapped to the key. If a platform-provided mapping for the key doesn't
* exist, this method creates a new mapping.
* <p>
* Specifying a {@code null} value clears the override, which restores the value mapped to
* the key to the platform-provided value. If the platform does not provide a mapping for
* the specified key, the mapping is effectively removed.
* <p>
* Calling this method does not update the mapping instantaneously; instead, the mapping
* is only updated after calling {@link #commit()}, or after the occurrence of an operating
* system event that causes the mapped value to be recomputed.
*
* @param key the key
* @param value the new value, or {@code null} to clear the override
* @throws NullPointerException if {@code key} is null
* @throws IllegalArgumentException if a platform-provided mapping for the key exists, and
* the specified value is an instance of a different class
* than the platform-provided value
* @return the previous value associated with {@code key}
*/
<T> T override(String key, T value);
/**
* Commits outstanding overridden preferences, which also causes the values of derived
* properties to be recomputed.
*/
void commit();
}
It is very likely the case that changing preferences can lead to very expensive operations in large real-world applications. For example, style themes or the entire user interface may be recreated, icons/images may be loaded, etc.
The `Preferences` implementation accounts for this by firing only a single invalidation notification, even when multiple platform preferences have changed. The documentation of `Preferences` contains guidance for users of this API:
* @implNote In many cases, multiple platform preferences can change at the same time.
* For example, switching from light to dark mode changes various foreground elements to light
* colors, and various background elements to dark colors.
* <p>
* The {@code Preferences} implementation returned from this method fires a single invalidation
* event for such bulk changes. If a listener performs potentially heavy work, such as recreating
* and applying CSS themes, it might be beneficial to use {@link javafx.beans.InvalidationListener}
* instead of {@link javafx.collections.MapChangeListener} to prevent the listener from firing
* multiple times in bulk change scenarios.
This works when the changes originate from the OS, but it doesn't work when an application overrides preference mappings manually. `ObservableMap` doesn't support bulk changes, so repeatedly calling `override(...)` would end up firing multiple change notifications, and subscribers would have no way of knowing when the bulk change transaction has ended.
That's where the concept of _uncommitted modifications_ comes into play: calling `override(...)` or any of the property setters like `setAppearance(...)` doesn't apply the changes immediately, it delays those changes until `commit()` is called or until an OS event causes the preference to be recomputed. When that happens, a single invalidation notification is fired, the same as it would have if the change originated from the OS.
-------------
PR Comment: https://git.openjdk.org/jfx/pull/1014#issuecomment-1502081527
More information about the openjfx-dev
mailing list