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