1 /* 2 * Copyright (c) 2010, 2013, 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 com.sun.javafx.FXAfter; 29 import com.sun.javafx.FXBefore; 30 import com.sun.javafx.FXTestThread; 31 import com.sun.javafx.FXThread; 32 import com.sun.javafx.FXUnit; 33 import javafx.geometry.Bounds; 34 import javafx.scene.Group; 35 import javafx.scene.Node; 36 import javafx.scene.Scene; 37 import javafx.scene.shape.Rectangle; 38 import javafx.stage.Stage; 39 import org.junit.Rule; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.Parameterized; 43 import org.junit.runners.Parameterized.Parameters; 44 45 import java.util.Arrays; 46 import java.util.Collection; 47 48 import static org.junit.Assert.*; 49 50 /** 51 * Tests for TraversalEngine with the default ContainerTabOrder algorithm, 52 * tests if using the WeightedClosestCorner algorithm have been 53 * left in comments. 54 */ 55 @RunWith(Parameterized.class) 56 public final class TraversalTest { 57 58 @Rule 59 public final FXUnit fx = new FXUnit(); 60 61 private final int fromNumber; 62 private final Direction direction; 63 private final int toNumber; 64 private final int toNumberTransformed; 65 66 private Stage stage; 67 private Scene scene; 68 /** 69 * 3x3 keypad. 70 * <p> 71 * Untransformed keypad: 72 * <ul> 73 * <li>1 2 3</li> 74 * <li>4 5 6</li> 75 * <li>7 8 9</li> 76 * </ul> 77 * <p> 78 * Transformed keypad: 79 * <ul> 80 * <li>7 4 1</li> 81 * <li>8 5 2</li> 82 * <li>9 6 3</li> 83 * </ul> 84 */ 85 private Node[] keypadNodes; 86 private TraversalEngine traversalEngine; 87 88 /* 89 * Parameters: [fromNumber], [direction], [toNumber], [toNumberTransformed] 90 */ 91 @Parameters 92 public static Collection data() { 93 return Arrays.asList(new Object[][] { 94 /* traversal from center */ 95 { 5, Direction.LEFT, 4, 8 }, 96 { 5, Direction.RIGHT, 6, 2 }, 97 { 5, Direction.UP, 2, 4 }, 98 { 5, Direction.DOWN, 8, 6 }, 99 100 // using WeightedClosestCorner, target varies according to transform 101 //{ 5, Direction.PREVIOUS, 4, 8 }, 102 //{ 5, Direction.NEXT, 6, 2 }, 103 104 // using ContainerTabOrder, target is always the same 105 { 5, Direction.PREVIOUS, 4, 4 }, 106 { 5, Direction.NEXT, 6, 6 }, 107 108 /* traversal from borders (untransformed) */ 109 { 4, Direction.LEFT, 4, 7 }, 110 { 6, Direction.RIGHT, 6, 3 }, 111 { 2, Direction.UP, 2, 1 }, 112 { 8, Direction.DOWN, 8, 9 }, 113 114 // using WeightedClosestCorner, target varies according to transform 115 //{ 4, Direction.PREVIOUS, 3, 7 }, 116 //{ 1, Direction.PREVIOUS, 9, 4 }, 117 //{ 6, Direction.NEXT, 7, 3 }, 118 //{ 9, Direction.NEXT, 1, 6 }, 119 120 // using ContainerTabOrder, target always the same 121 { 4, Direction.PREVIOUS, 3, 3 }, 122 { 1, Direction.PREVIOUS, 9, 9 }, 123 { 6, Direction.NEXT, 7, 7 }, 124 { 9, Direction.NEXT, 1, 1 }, 125 126 /* traversal from borders (transformed) */ 127 { 2, Direction.RIGHT, 3, 2 }, 128 { 8, Direction.LEFT, 7, 8 }, 129 { 4, Direction.UP, 1, 4 }, 130 { 6, Direction.DOWN, 9, 6 }, 131 132 // using WeightedClosestCorner, target varies according to transform 133 //{ 8, Direction.PREVIOUS, 7, 1 }, 134 //{ 7, Direction.PREVIOUS, 6, 3 }, 135 //{ 2, Direction.NEXT, 3, 9 }, 136 //{ 3, Direction.NEXT, 4, 7 } 137 138 // using ContainerTabOrder, target always the same 139 { 8, Direction.PREVIOUS, 7, 7 }, 140 { 7, Direction.PREVIOUS, 6, 6 }, 141 { 2, Direction.NEXT, 3, 3 }, 142 { 3, Direction.NEXT, 4, 4 } 143 }); 144 } 145 146 public TraversalTest(final int fromNumber, 147 final Direction direction, 148 final int toNumber, 149 final int toNumberTransformed) { 150 this.fromNumber = fromNumber; 151 this.direction = direction; 152 this.toNumber = toNumber; 153 this.toNumberTransformed = toNumberTransformed; 154 } 155 156 @FXBefore 157 public void setUp() { 158 stage = new Stage(); 159 scene = new Scene(new Group(), 500, 500); 160 stage.setScene(scene); 161 162 traversalEngine = new TraversalEngine(scene.getRoot(), true); 163 164 keypadNodes = createKeypadNodesInScene(scene, traversalEngine); 165 166 stage.show(); 167 stage.requestFocus(); 168 } 169 170 @FXAfter 171 public void tearDown() { 172 stage = null; 173 scene = null; 174 keypadNodes = null; 175 traversalEngine = null; 176 } 177 178 @Test 179 @FXTestThread(FXThread.MAIN) 180 public void untransformedTraversalTest() { 181 fx.invokeAndWait(() -> { 182 keypadNodes[fromNumber - 1].requestFocus(); 183 traversalEngine.trav(keypadNodes[fromNumber - 1], direction); 184 }); 185 fx.waitForNextPulse(); 186 fx.invokeAndWait( 187 () -> assertTrue(keypadNodes[toNumber - 1].isFocused())); 188 } 189 190 @Test 191 public void transformedTraversalTest() { 192 scene.getRoot().setRotate(90); 193 keypadNodes[fromNumber - 1].requestFocus(); 194 traversalEngine.trav(keypadNodes[fromNumber - 1], direction); 195 assertTrue(keypadNodes[toNumberTransformed - 1].isFocused()); 196 } 197 198 @Test 199 public void traverseListenerTest() { 200 final TraverseListenerImpl traverseListener = 201 new TraverseListenerImpl(); 202 traversalEngine.addTraverseListener(traverseListener); 203 keypadNodes[fromNumber - 1].requestFocus(); 204 traversalEngine.trav(keypadNodes[fromNumber - 1], direction); 205 if (fromNumber != toNumber) { 206 assertEquals(1, traverseListener.getCallCounter()); 207 assertSame(keypadNodes[toNumber - 1], 208 traverseListener.getLastNode()); 209 } else { 210 assertEquals(0, traverseListener.getCallCounter()); 211 } 212 } 213 214 private static Node[] createKeypadNodesInScene( 215 final Scene scene, 216 final TraversalEngine traversalEngine) { 217 final Node[] keypad = new Node[9]; 218 219 int index = 0; 220 for (int row = 0; row < 3; ++row) { 221 for (int column = 0; column < 3; ++column) { 222 final Node keyNode = new Rectangle(10 + column * 50, 223 10 + row * 50, 224 40, 40); 225 keyNode.setFocusTraversable(true); 226 227 keypad[index++] = keyNode; 228 ((Group)scene.getRoot()).getChildren().add(keyNode); 229 traversalEngine.reg(keyNode); 230 } 231 } 232 233 return keypad; 234 } 235 236 private static final class TraverseListenerImpl 237 implements TraverseListener { 238 private int callCounter; 239 private Node lastNode; 240 241 public int getCallCounter() { 242 return callCounter; 243 } 244 245 public Node getLastNode() { 246 return lastNode; 247 } 248 249 @Override 250 public void onTraverse(final Node node, final Bounds bounds) { 251 ++callCounter; 252 lastNode = node; 253 } 254 } 255 }