diff -r a02d30f297f1 ga-samples/BouncingBalls/src/balls/BallsPane.java --- a/ga-samples/BouncingBalls/src/balls/BallsPane.java Wed May 14 13:07:33 2014 -0700 +++ b/ga-samples/BouncingBalls/src/balls/BallsPane.java Fri Jun 20 14:44:39 2014 -0700 @@ -75,6 +75,12 @@ return rectangle; } + public void initAccessibility(){ + for(Ball b: balls){ + b.setFocusTraversable(true); + } + } + public void resetBalls(){ for(Ball b: balls){ b.reset(); diff -r a02d30f297f1 ga-samples/BouncingBalls/src/balls/component/Ball.java --- a/ga-samples/BouncingBalls/src/balls/component/Ball.java Wed May 14 13:07:33 2014 -0700 +++ b/ga-samples/BouncingBalls/src/balls/component/Ball.java Fri Jun 20 14:44:39 2014 -0700 @@ -37,8 +37,9 @@ import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; -import javafx.event.EventHandler; -import javafx.scene.input.MouseEvent; +import javafx.scene.accessibility.Action; +import javafx.scene.accessibility.Attribute; +import javafx.scene.accessibility.Role; import javafx.scene.shape.Circle; import javafx.util.Duration; @@ -47,11 +48,21 @@ private static final Duration DURATION = Duration.millis(850); private static final double FLOOR = HEIGHT / 2 - HEIGHT_CORRECTION - 10; private Timeline timeline; - + + private int index; public Ball(final int index) { + this.index = index; initBall(index); } + private void ping() { + if (timeline.getStatus() != Status.RUNNING) { + timeline.play(); + } else { + timeline.pause(); + } + } + private void initBall(final int index) { setRadius(BALL_RADIUS); setCenterX((index + 1) * 2 * BALL_RADIUS + index * SPACE_X); @@ -59,16 +70,7 @@ setCache(true); setFill(BALL_GRADIENT); createTimeline(); - setOnMousePressed(new EventHandler() { - public void handle(MouseEvent me) { - if (timeline.getStatus() != Status.RUNNING) { - timeline.play(); - } else { - timeline.pause(); - } - } - }); - + setOnMousePressed(e -> ping()); } @@ -89,4 +91,32 @@ timeline.stop(); setTranslateY(0); } + + @Override + public Object accGetAttribute(Attribute attribute, Object... parameters) { + switch (attribute) { + case ROLE: return Role.BUTTON; + case DESCRIPTION: return "ball"; //so narrator reads 'ball' instead of 'button'. Should be localized. + case TITLE: { + switch (index) { + case 0: return "first"; + case 1: return "second"; + case 2: return "thrid"; + case 3: return "fourth"; + case 4: return "fifth"; + default: return "no name"; + } + } + case TOOLTIP: return "use primary action to start animation"; + default: return super.accGetAttribute(attribute, parameters); + } + } + + @Override + public void accExecuteAction(Action action, Object... parameters) { + switch (action) { + case FIRE: ping(); + default: super.accExecuteAction(action, parameters); + } + } } diff -r a02d30f297f1 ga-samples/BouncingBalls/src/balls/component/InfoPanel.java --- a/ga-samples/BouncingBalls/src/balls/component/InfoPanel.java Wed May 14 13:07:33 2014 -0700 +++ b/ga-samples/BouncingBalls/src/balls/component/InfoPanel.java Fri Jun 20 14:44:39 2014 -0700 @@ -37,6 +37,7 @@ import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.Parent; +import javafx.scene.accessibility.Attribute; import javafx.scene.control.Button; import javafx.scene.paint.Color; import javafx.scene.text.Text; @@ -67,7 +68,21 @@ } private Node createResetButton() { - final Button btn = new Button("Reset"); + final Button btn = new Button("Reset") { + private boolean a11yInit = false; + @Override + public Object accGetAttribute(Attribute attribute, Object... parameters) { + if (!a11yInit) { + a11yInit = true; + //Make the balls focus traversable when a screen reader is active + ballsPane.initAccessibility(); + } + if (attribute == Attribute.TOOLTIP) { + return "reset all balls to their initial position"; + } + return super.accGetAttribute(attribute, parameters); + } + }; btn.setPrefSize(BUTTON_WIDTH, BUTTON_HEIGHT); btn.setOnAction(new EventHandler() {