|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
4 | 4 | *
|
5 | 5 | * This code is free software; you can redistribute it and/or modify it
|
|
28 | 28 | import java.util.ArrayList;
|
29 | 29 | import java.util.Collections;
|
30 | 30 | import java.util.List;
|
31 |
| - |
32 |
| -import com.sun.javafx.scene.control.skin.Utils; |
33 |
| - |
34 | 31 | import javafx.animation.Animation;
|
35 | 32 | import javafx.animation.KeyFrame;
|
36 | 33 | import javafx.application.Platform;
|
|
40 | 37 | import javafx.beans.property.SimpleBooleanProperty;
|
41 | 38 | import javafx.beans.property.StringProperty;
|
42 | 39 | import javafx.beans.property.StringPropertyBase;
|
| 40 | +import javafx.beans.value.ChangeListener; |
| 41 | +import javafx.beans.value.ObservableValue; |
43 | 42 | import javafx.beans.value.WritableValue;
|
44 | 43 | import javafx.collections.ObservableList;
|
| 44 | +import javafx.css.CssMetaData; |
| 45 | +import javafx.css.Styleable; |
| 46 | +import javafx.css.StyleableBooleanProperty; |
| 47 | +import javafx.css.StyleableObjectProperty; |
| 48 | +import javafx.css.StyleableProperty; |
| 49 | +import javafx.css.converter.BooleanConverter; |
| 50 | +import javafx.css.converter.EnumConverter; |
45 | 51 | import javafx.geometry.Pos;
|
46 | 52 | import javafx.geometry.Side;
|
47 | 53 | import javafx.scene.Node;
|
| 54 | +import javafx.scene.Scene; |
48 | 55 | import javafx.scene.control.Label;
|
49 | 56 | import javafx.scene.layout.Pane;
|
50 | 57 | import javafx.scene.layout.Region;
|
51 |
| - |
| 58 | +import javafx.stage.Window; |
| 59 | +import javafx.util.Subscription; |
52 | 60 | import com.sun.javafx.charts.ChartLayoutAnimator;
|
53 | 61 | import com.sun.javafx.charts.Legend;
|
54 | 62 | import com.sun.javafx.scene.NodeHelper;
|
55 |
| - |
56 |
| -import javafx.css.StyleableBooleanProperty; |
57 |
| -import javafx.css.StyleableObjectProperty; |
58 |
| -import javafx.css.CssMetaData; |
59 |
| - |
60 |
| -import javafx.css.converter.BooleanConverter; |
61 |
| -import javafx.css.converter.EnumConverter; |
62 |
| - |
63 |
| -import javafx.css.Styleable; |
64 |
| -import javafx.css.StyleableProperty; |
| 63 | +import com.sun.javafx.scene.control.skin.Utils; |
65 | 64 |
|
66 | 65 | /**
|
67 | 66 | * Base class for all charts. It has 3 parts the title, legend and chartContent. The chart content is populated by the
|
@@ -104,6 +103,9 @@ public abstract class Chart extends Region {
|
104 | 103 | /** Animator for animating stuff on the chart */
|
105 | 104 | private final ChartLayoutAnimator animator = new ChartLayoutAnimator(chartContent);
|
106 | 105 |
|
| 106 | + // SimpleBooleanProperty or Subscription |
| 107 | + private volatile Object accessibilityActive; |
| 108 | + |
107 | 109 | // -------------- PUBLIC PROPERTIES --------------------------------------------------------------------------------
|
108 | 110 |
|
109 | 111 | /** The chart title */
|
@@ -274,7 +276,6 @@ protected ObservableList<Node> getChartChildren() {
|
274 | 276 | */
|
275 | 277 | public Chart() {
|
276 | 278 | titleLabel.setAlignment(Pos.CENTER);
|
277 |
| - titleLabel.focusTraversableProperty().bind(Platform.accessibilityActiveProperty()); |
278 | 279 | getChildren().addAll(titleLabel, chartContent);
|
279 | 280 | getStyleClass().add("chart");
|
280 | 281 | titleLabel.getStyleClass().add("chart-title");
|
@@ -511,6 +512,62 @@ public StyleableProperty<Boolean> getStyleableProperty(Chart node) {
|
511 | 512 | return getClassCssMetaData();
|
512 | 513 | }
|
513 | 514 |
|
514 |
| -} |
| 515 | + private void handleAccessibilityActive(boolean on) { |
| 516 | + titleLabel.setFocusTraversable(on); |
| 517 | + updateSymbolFocusable(on); |
| 518 | + } |
515 | 519 |
|
| 520 | + /** |
| 521 | + * Invoked in the FX application thread when accessibility active platform property changes. |
| 522 | + * The child classes should override this method to set focus traversable flag on every symbol, as well as |
| 523 | + * other elements that need to be focusable. |
| 524 | + * @param on whether the accessibility is active |
| 525 | + */ |
| 526 | + // package protected: custom charts must handle accessbility on their own |
| 527 | + void updateSymbolFocusable(boolean on) { |
| 528 | + } |
516 | 529 |
|
| 530 | + /** |
| 531 | + * When called from JavaFX application thread, returns the value of the property. |
| 532 | + * When called from any other thread, returns false and sets up the machinery to |
| 533 | + * invoke {@code updateSymbolFocusable()} when needed. |
| 534 | + * The chart implementations should use this method to set focus travesable flags on the nodes |
| 535 | + * which needs to be focus traversable when accessibility is on. |
| 536 | + * @return |
| 537 | + */ |
| 538 | + // package protected: custom charts must handle accessbility on their own |
| 539 | + final boolean isAccessibilityActive() { |
| 540 | + if (Platform.isFxApplicationThread()) { |
| 541 | + if (accessibilityActive instanceof SimpleBooleanProperty p) { |
| 542 | + return p.get(); |
| 543 | + } else { |
| 544 | + if (accessibilityActive instanceof Subscription sub) { |
| 545 | + sub.unsubscribe(); |
| 546 | + } |
| 547 | + SimpleBooleanProperty active = new SimpleBooleanProperty(); |
| 548 | + accessibilityActive = active; |
| 549 | + active.addListener((src, prev, on) -> { |
| 550 | + handleAccessibilityActive(on); |
| 551 | + }); |
| 552 | + active.bind(Platform.accessibilityActiveProperty()); |
| 553 | + return active.get(); |
| 554 | + } |
| 555 | + } else { |
| 556 | + // chart and its data are allowed to be constructed in a background thread |
| 557 | + if (accessibilityActive == null) { |
| 558 | + // set up a subscription to be fired once the chart becomes a part of the scene graph |
| 559 | + accessibilityActive = sceneProperty() |
| 560 | + .flatMap(Scene::windowProperty) |
| 561 | + .subscribe((w) -> { |
| 562 | + if (w != null) { |
| 563 | + // will unsubscribe when appears in a window, in the fx app thread |
| 564 | + if (isAccessibilityActive()) { |
| 565 | + handleAccessibilityActive(true); |
| 566 | + } |
| 567 | + } |
| 568 | + }); |
| 569 | + } |
| 570 | + return false; |
| 571 | + } |
| 572 | + } |
| 573 | +} |
0 commit comments