1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.scene.traversal;
  27 
  28 import static org.junit.Assert.assertEquals;
  29 import static org.junit.Assert.assertSame;
  30 import static org.junit.Assert.assertTrue;
  31 
  32 import java.util.Arrays;
  33 import java.util.Collection;
  34 
  35 import javafx.geometry.Bounds;
  36 import javafx.scene.Group;
  37 import javafx.scene.Node;
  38 import javafx.scene.Scene;
  39 import javafx.scene.shape.Rectangle;
  40 import javafx.stage.Stage;
  41 
  42 import org.junit.After;
  43 import org.junit.Before;
  44 import org.junit.Test;
  45 import org.junit.runner.RunWith;
  46 import org.junit.runners.Parameterized;
  47 import org.junit.runners.Parameterized.Parameters;
  48 
  49 /**
  50  * Tests for TraversalEngine with the default ContainerTabOrder algorithm,
  51  * tests if using the WeightedClosestCorner algorithm have been
  52  * left in comments.
  53  */
  54 @RunWith(Parameterized.class)
  55 public final class TraversalTest {
  56     private final int fromNumber;
  57     private final Direction direction;
  58     private final int toNumber;
  59     private final int toNumberTransformed;
  60 
  61     private Stage stage;
  62     private Scene scene;
  63     /**
  64      * 3x3 keypad.
  65      * <p>
  66      * Untransformed keypad:
  67      * <ul>
  68      *   <li>1 2 3</li>
  69      *   <li>4 5 6</li>
  70      *   <li>7 8 9</li>
  71      * </ul>
  72      * <p>
  73      * Transformed keypad:
  74      * <ul>
  75      *   <li>7 4 1</li>
  76      *   <li>8 5 2</li>
  77      *   <li>9 6 3</li>
  78      * </ul>
  79      */
  80     private Node[] keypadNodes;
  81     private SceneTraversalEngine traversalEngine;
  82 
  83     /*
  84      * Parameters: [fromNumber], [direction], [toNumber], [toNumberTransformed]
  85      */
  86     @Parameters
  87     public static Collection data() {
  88         return Arrays.asList(new Object[][] {
  89             /* traversal from center */
  90             { 5, Direction.LEFT, 4, 8 },
  91             { 5, Direction.RIGHT, 6, 2 },
  92             { 5, Direction.UP, 2, 4 },
  93             { 5, Direction.DOWN, 8, 6 },
  94 
  95             // using WeightedClosestCorner, target varies according to transform
  96             //{ 5, Direction.PREVIOUS, 4, 8 },
  97             //{ 5, Direction.NEXT, 6, 2 },
  98 
  99             // using ContainerTabOrder, target is always the same
 100             { 5, Direction.PREVIOUS, 4, 4 },
 101             { 5, Direction.NEXT, 6, 6 },
 102 
 103             /* traversal from borders (untransformed) */
 104             { 4, Direction.LEFT, 4, 7 },
 105             { 6, Direction.RIGHT, 6, 3 },
 106             { 2, Direction.UP, 2, 1 },
 107             { 8, Direction.DOWN, 8, 9 },
 108 
 109             // using WeightedClosestCorner, target varies according to transform
 110             //{ 4, Direction.PREVIOUS, 3, 7 },
 111             //{ 1, Direction.PREVIOUS, 9, 4 },
 112             //{ 6, Direction.NEXT, 7, 3 },
 113             //{ 9, Direction.NEXT, 1, 6 },
 114 
 115             // using ContainerTabOrder, target always the same
 116             { 4, Direction.PREVIOUS, 3, 3 },
 117             { 1, Direction.PREVIOUS, 9, 9 },
 118             { 6, Direction.NEXT, 7, 7 },
 119             { 9, Direction.NEXT, 1, 1 },
 120 
 121             /* traversal from borders (transformed) */
 122             { 2, Direction.RIGHT, 3, 2 },
 123             { 8, Direction.LEFT, 7, 8 },
 124             { 4, Direction.UP, 1, 4 },
 125             { 6, Direction.DOWN, 9, 6 },
 126 
 127             // using WeightedClosestCorner, target varies according to transform
 128             //{ 8, Direction.PREVIOUS, 7, 1 },
 129             //{ 7, Direction.PREVIOUS, 6, 3 },
 130             //{ 2, Direction.NEXT, 3, 9 },
 131             //{ 3, Direction.NEXT, 4, 7 }
 132 
 133             // using ContainerTabOrder, target always the same
 134             { 8, Direction.PREVIOUS, 7, 7 },
 135             { 7, Direction.PREVIOUS, 6, 6 },
 136             { 2, Direction.NEXT, 3, 3 },
 137             { 3, Direction.NEXT, 4, 4 }
 138         });
 139     }
 140 
 141     public TraversalTest(final int fromNumber,
 142                          final Direction direction,
 143                          final int toNumber,
 144                          final int toNumberTransformed) {
 145         this.fromNumber = fromNumber;
 146         this.direction = direction;
 147         this.toNumber = toNumber;
 148         this.toNumberTransformed = toNumberTransformed;
 149     }
 150 
 151     @Before
 152     public void setUp() {
 153         stage = new Stage();
 154         scene = new Scene(new Group(), 500, 500);
 155         stage.setScene(scene);
 156 
 157         traversalEngine = new SceneTraversalEngine(scene);
 158 
 159         keypadNodes = createKeypadNodesInScene(scene, traversalEngine);
 160 
 161         stage.show();
 162         stage.requestFocus();
 163     }
 164 
 165     @After
 166     public void tearDown() {
 167         stage = null;
 168         scene = null;
 169         keypadNodes = null;
 170         traversalEngine = null;
 171     }
 172 
 173     @Test
 174     public void untransformedTraversalTest() {
 175         keypadNodes[fromNumber - 1].requestFocus();
 176         traversalEngine.trav(keypadNodes[fromNumber - 1], direction);
 177         assertTrue(keypadNodes[toNumber - 1].isFocused());
 178     }
 179 
 180     @Test
 181     public void transformedTraversalTest() {
 182         scene.getRoot().setRotate(90);
 183         keypadNodes[fromNumber - 1].requestFocus();
 184         traversalEngine.trav(keypadNodes[fromNumber - 1], direction);
 185         assertTrue(keypadNodes[toNumberTransformed - 1].isFocused());
 186     }
 187 
 188     @Test
 189     public void traverseListenerTest() {
 190         final TraverseListenerImpl traverseListener =
 191                 new TraverseListenerImpl();
 192         traversalEngine.addTraverseListener(traverseListener);
 193         keypadNodes[fromNumber - 1].requestFocus();
 194         traversalEngine.trav(keypadNodes[fromNumber - 1], direction);
 195         if (fromNumber != toNumber) {
 196             assertEquals(1, traverseListener.getCallCounter());
 197             assertSame(keypadNodes[toNumber - 1],
 198                        traverseListener.getLastNode());
 199         } else {
 200             assertEquals(0, traverseListener.getCallCounter());
 201         }
 202     }
 203 
 204     private static Node[] createKeypadNodesInScene(
 205             final Scene scene,
 206             final TraversalEngine traversalEngine) {
 207         final Node[] keypad = new Node[9];
 208 
 209         int index = 0;
 210         for (int row = 0; row < 3; ++row) {
 211             for (int column = 0; column < 3; ++column) {
 212                 final Node keyNode = new Rectangle(10 + column * 50,
 213                                                    10 + row * 50,
 214                                                    40, 40);
 215                 keyNode.setFocusTraversable(true);
 216 
 217                 keypad[index++] = keyNode;
 218                 ((Group)scene.getRoot()).getChildren().add(keyNode);
 219             }
 220         }
 221 
 222         return keypad;
 223     }
 224 
 225     private static final class TraverseListenerImpl
 226             implements TraverseListener {
 227         private int callCounter;
 228         private Node lastNode;
 229 
 230         public int getCallCounter() {
 231             return callCounter;
 232         }
 233         
 234         public Node getLastNode() {
 235             return lastNode;
 236         }
 237 
 238         @Override
 239         public void onTraverse(final Node node, final Bounds bounds) {
 240             ++callCounter;
 241             lastNode = node;
 242         }
 243     }
 244 }