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, since the `transition` property is set by both the old and the new 
style map, we don't reset it: its new value will be calculated in the next 
`Node.applyCss()` pass.

When a property is set by CSS, we also need to make sure that when we ask "has 
the property actually changed?" we compare the calculated value to the 
_after-change style_ value of the property, and not to its current (potentially 
animating) value.

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

Commit messages:
 - fix

Changes: https://git.openjdk.org/jfx/pull/2038/files
  Webrev: https://webrevs.openjdk.org/?repo=jfx&pr=2038&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8375363
  Stats: 403 lines in 10 files changed: 357 ins; 16 del; 30 mod
  Patch: https://git.openjdk.org/jfx/pull/2038.diff
  Fetch: git fetch https://git.openjdk.org/jfx.git pull/2038/head:pull/2038

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

Reply via email to