RFR: 8375363: StyleHelper.resetToInitialValues() interrupts animated properties
Andy Goryachev
angorya at openjdk.org
Thu Jan 15 19:51:45 UTC 2026
On Thu, 15 Jan 2026 03:11:11 GMT, Michael Strauß <mstrauss at openjdk.org> wrote:
> In the following program, resizing the window to be smaller than 300px should start a transition for `-fx-scale-x` from 2 to 1, but instead the value sometimes snaps back to 1 instantly:
>
>
> public class InterruptedTransitionBug extends Application {
> @Override
> public void start(Stage stage) throws Exception {
> var root = new StackPane(new Button("My Button"));
> var scene = new Scene(root, 600, 200);
> scene.getStylesheets().add("data:text/css;base64," + Base64.getEncoder().encodeToString("""
> .button {
> transition: -fx-scale-x 2s;
> }
>
> @media (width > 300px) {
> .button {
> -fx-scale-x: 2;
> }
> }
> """.getBytes(StandardCharsets.UTF_8)));
> stage.setScene(scene);
> stage.show();
> }
> }
>
>
> ### Problem
> First, let's look at how `CssStyleHelper` works when the cascading style map for a node changes:
> 1. When the style map for a node has changed, `StyleHelper.createStyleHelper(Node)` determines that the style helper is no longer any good, and a new `CssStyleHelper` for the new style map is created.
> 2. Before the old style helper is discarded, it calls `CssStyleHelper.resetToInitialValues(Styleable)` and resets all properties that were set by this style helper back to their initial values. This ensures that the new style helper has a "clean slate" to work from.
> 3. The next `Node.applyCss()` pass will now set the properties that are specified in the new style map to their new values.
>
> However, when transitions come into play, this mechanism can break:
> 1. In our example, the `Button` starts with a style map that contains two properties:
> * `transition: -fx-scale-x 2s`
> * `-fx-scale-x: 2`
> 2. Due to a changing media query, this is replaced with a style map that only contains a single entry:
> * `transition: -fx-scale-x 2s`
> 3. Before the new style map is applied, all properties affected by the old style map are reset to their initial values. That means:
> * `-fx-scale-x` is reset to `1`
> * `transition` is reset to `null`
>
> This is where it breaks: if `transition` is reset before `-fx-scale-x`, the latter will see no transition when its value is set back to `1`.
>
> ### Solution
> Simply resetting all properties back to their initial values when a style map is changed is not necessary. We only need to reset the properties that are set by the old style map, but are no longer set by the new style map. In our example, si...
Marked as reviewed by angorya (Reviewer).
modules/javafx.graphics/src/main/java/javafx/css/StyleableBooleanProperty.java line 57:
> 55: StyleablePropertyHelper.setBooleanAccessor(new StyleablePropertyHelper.Accessor() {
> 56: @Override
> 57: public boolean equalsAfterChangeStyleValue(StyleableProperty<?> property, Object value) {
minor: would it be slightly more organized to call a private instance method of the property class from each accessor?
I think the code is ok, it's just adds so much nearly identical code that bothers me, though I can't figure out a way to avoid the `Accessor` pattern here, package protected methods would not work, would they?
-------------
PR Review: https://git.openjdk.org/jfx/pull/2038#pullrequestreview-3667269954
PR Review Comment: https://git.openjdk.org/jfx/pull/2038#discussion_r2695711152
More information about the openjfx-dev
mailing list