1 /* 2 * Copyright (c) 2013, 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.glass.ui.monocle; 27 28 import com.sun.glass.ui.monocle.input.devices.TestTouchDevice; 29 import com.sun.glass.ui.monocle.input.devices.TestTouchDevices; 30 import javafx.geometry.BoundingBox; 31 import javafx.geometry.Bounds; 32 import javafx.geometry.Point2D; 33 import javafx.scene.Node; 34 import javafx.scene.input.MouseEvent; 35 import javafx.scene.shape.Rectangle; 36 import org.junit.Before; 37 import org.junit.Ignore; 38 import org.junit.Test; 39 import org.junit.runners.Parameterized; 40 41 import java.util.Collection; 42 import java.util.concurrent.atomic.AtomicReference; 43 44 public class TouchButtonTest extends ParameterizedTestBase { 45 46 private Node button1; 47 private Node button2; 48 private Node button3; 49 50 public TouchButtonTest(TestTouchDevice device) { 51 super(device); 52 } 53 54 @Parameterized.Parameters 55 public static Collection<Object[]> data() { 56 return TestTouchDevices.getTouchDeviceParameters(1); 57 } 58 59 public Node createButton(String text, int x, int y, boolean setListeners) { 60 final Node button = new Rectangle(100.0, 20.0); 61 button.setId(text); 62 button.setLayoutX(x); 63 button.setLayoutY(y); 64 button.setOnMousePressed((e) -> button.requestFocus()); 65 if (setListeners) { 66 button.addEventHandler(MouseEvent.ANY, e -> 67 TestLog.log(e.getEventType().getName() +": " 68 + (int) e.getScreenX() 69 + ", " + (int) e.getScreenY())); 70 button.focusedProperty().addListener((observable, oldValue, newValue) -> 71 TestLog.log(button.getId() + " isFocused=" + newValue)); 72 } 73 74 return button; 75 } 76 77 @Before 78 public void createButtons() throws Exception { 79 TestRunnable.invokeAndWait(() -> { 80 int X = (int) width / 2; 81 int Y = (int) height / 2; 82 83 button1 = createButton("button1", X, Y - 100, true); 84 button2 = createButton("button2", X, Y + 100, true); 85 button3 = createButton("button3", 0, 0, false); 86 87 TestApplication.getRootGroup().getChildren().clear(); 88 TestApplication.getRootGroup().getChildren().addAll( 89 button1, button2, button3); 90 button3.requestFocus(); 91 }); 92 93 TestApplication.waitForLayout(); 94 } 95 96 /** 97 * Tests 98 */ 99 100 @Test 101 public void tapOnButton() throws Exception { 102 Point2D clickAt = tapInsideButton(button1); 103 waitForFocusGainOn("button1"); 104 waitForMouseEnteredAt(clickAt); 105 waitForMouseClickAt(clickAt); 106 } 107 108 @Test 109 public void tapOn2Buttons() throws Exception { 110 Point2D clickAt = tapInsideButton(button1); 111 waitForFocusGainOn("button1"); 112 waitForMouseEnteredAt(clickAt); 113 waitForMouseClickAt(clickAt); 114 115 clickAt = tapInsideButton(button2); 116 waitForFocusLostOn("button1"); 117 waitForFocusGainOn("button2"); 118 waitForMouseEnteredAt(clickAt); 119 waitForMouseClickAt(clickAt); 120 } 121 122 @Test 123 public void tapOutAndInButton() throws Exception { 124 tapOutSideButton(); 125 TestLog.reset(); 126 Point2D clickAt = tapInsideButton(button1); 127 waitForMouseClickAt(clickAt); 128 waitForFocusGainOn("button1"); 129 } 130 131 @Test 132 public void tapOutInAndOutButton() throws Exception { 133 tapOutSideButton(); 134 TestLog.reset(); 135 Point2D clickAt = tapInsideButton(button1); 136 waitForMouseClickAt(clickAt); 137 waitForFocusGainOn("button1"); 138 139 tapOutSideButton(); 140 tapInsideButton(button3); 141 waitForFocusLostOn("button1"); 142 } 143 144 @Test 145 public void tapInAndOutLoop() throws Exception { 146 tapOutSideButton(); 147 TestLog.reset(); 148 for (int i = 0 ; i < 2 ; i++) { 149 tapOutSideButton(); 150 tapInsideButton(button3); 151 TestLog.reset(); 152 Point2D clickAt = tapInsideButton(button1); 153 waitForFocusGainOn("button1"); 154 waitForMouseEnteredAt(clickAt); 155 waitForMouseClickAt(clickAt); 156 157 tapOutSideButton(); 158 tapInsideButton(button3); 159 waitForFocusLostOn("button1"); 160 TestLog.reset(); 161 162 clickAt = tapInsideButton(button2); 163 waitForFocusGainOn("button2"); 164 waitForMouseEnteredAt(clickAt); 165 166 waitForMouseClickAt(clickAt); 167 TestLog.reset(); 168 tapOutSideButton(); 169 tapInsideButton(button3); 170 waitForFocusLostOn("button2"); 171 } 172 } 173 174 /** 175 * RT-34625 - we should get a click when tapping on a control, dragging the 176 * finger and release the finger inside the control 177 */ 178 @Test 179 public void tapAndDrag() throws Exception { 180 Bounds buttonBounds = getButtonBounds(button2); 181 182 //start at right most x and center y 183 int x = (int) buttonBounds.getMaxX() - 1; 184 int y = (int) (buttonBounds.getMinY() + buttonBounds.getMaxY()) / 2; 185 186 ///tap 187 int p = device.addPoint(x, y); 188 device.sync(); 189 190 waitForFocusGainOn("button2"); 191 192 //drag inside button 193 for (; x > buttonBounds.getMinX(); x-- ) { 194 device.setPoint(p, x, y); 195 device.sync(); 196 } 197 198 //release inside the button 199 device.removePoint(p); 200 device.sync(); 201 TestLog.waitForLogContaining("MOUSE_CLICKED:", 3000l); 202 TestLog.waitForLogContaining("MOUSE_RELEASED:", 3000l); 203 } 204 205 /** 206 * RT-34625 - Currently a control will not generate a click when tapping on 207 * it, drag the finger outside the control and release the finger. 208 * This might be a desired behavior, but sometime there are small 209 * unintentional drags that resulting in a finger release outside the 210 * control. 211 * 212 * This test should fail and throw RuntimeException 213 * 214 */ 215 @Ignore("RT-34625") 216 @Test 217 public void tapAndDrag_fail() throws Exception { 218 Bounds buttonBounds = getButtonBounds(button2); 219 220 //start at right most x and center y 221 int x = (int) buttonBounds.getMaxX() - 1; 222 int y = (int) (buttonBounds.getMinY() + buttonBounds.getMaxY()) / 2; 223 224 ///tap 225 int p = device.addPoint(x, y); 226 device.sync(); 227 228 waitForFocusGainOn("button2"); 229 230 //drag outside button 231 for (; x > buttonBounds.getMinX() - device.getTapRadius() - 10; x-- ) { 232 device.setPoint(p, x, y); 233 device.sync(); 234 } 235 236 //release outside the button 237 device.removePoint(p); 238 device.sync(); 239 TestLog.waitForLogContaining("MOUSE_CLICKED:", 3000l); 240 } 241 242 @Test 243 public void tapping_oneButtonOnScreen () throws Exception { 244 AtomicReference<Node> buttonRef = new AtomicReference<>(); 245 TestRunnable.invokeAndWait(() -> { 246 Node button4 = createButton("button4", 0, 0, true); 247 buttonRef.set(button4); 248 TestApplication.getRootGroup().getChildren().clear(); 249 TestApplication.getRootGroup().getChildren().addAll(button4); 250 }); 251 TestApplication.waitForLayout(); 252 253 for (int i = 0; i < 5; i++) { 254 Point2D clickAt = tapInsideButton(buttonRef.get()); 255 waitForMouseClickAt(clickAt); 256 TestLog.reset(); 257 } 258 } 259 260 /** utilities */ 261 public Bounds getButtonBounds(Node button) throws Exception { 262 AtomicReference<Bounds> ref = new AtomicReference<>(); 263 TestRunnable.invokeAndWait(() -> { 264 ref.set(button.localToScreen( 265 new BoundingBox(0, 0, 266 button.getBoundsInParent().getWidth(), 267 button.getBoundsInParent().getHeight()))); 268 TestLog.log("Bounds for " + button.getId() + " are " + ref.get()); 269 }); 270 return ref.get(); 271 } 272 273 public Point2D getCenterOfButton(Node button) throws Exception { 274 Bounds buttonBounds = getButtonBounds(button); 275 Point2D clickAt = new Point2D( 276 buttonBounds.getMinX()+ buttonBounds.getWidth() / 2, 277 buttonBounds.getMinY()+ buttonBounds.getHeight() / 2); 278 return clickAt; 279 } 280 281 public Point2D tapInsideButton(Node button) throws Exception { 282 Point2D clickAt = getCenterOfButton(button); 283 //tap 284 int p = device.addPoint(clickAt.getX(), clickAt.getY()); 285 device.sync(); 286 //release 287 device.removePoint(p); 288 device.sync(); 289 TestLog.waitForLog("Mouse clicked: %.0f, %.0f", clickAt.getX(), clickAt.getY()); 290 return clickAt; 291 } 292 293 public void tapOutSideButton() throws Exception { 294 Bounds buttonBounds = getButtonBounds(button3); 295 ///tap 296 double x = buttonBounds.getMaxX() + device.getTapRadius() + 10; 297 double y = buttonBounds.getMaxY() + device.getTapRadius() + 10; 298 int p = device.addPoint(x, y); 299 device.sync(); 300 //release 301 device.removePoint(p); 302 device.sync(); 303 TestLog.waitForLog("Mouse clicked: %.0f, %.0f", x, y); 304 } 305 306 public void waitForMouseClickAt(Point2D clickAt) throws Exception{ 307 TestLog.waitForLog("MOUSE_CLICKED: %d, %d", 308 Math.round(clickAt.getX()), 309 Math.round(clickAt.getY())); 310 } 311 312 public void waitForMouseEnteredAt(Point2D clickAt) throws Exception{ 313 TestLog.waitForLog("MOUSE_ENTERED: %d, %d", 314 Math.round(clickAt.getX()), 315 Math.round(clickAt.getY())); 316 } 317 318 public void waitForFocus(String id, boolean focusState) throws Exception { 319 TestLog.waitForLog("%s isFocused=%b", id, focusState); 320 } 321 322 public void waitForFocusGainOn(String id) throws Exception{ 323 waitForFocus(id, true); 324 } 325 326 public void waitForFocusLostOn(String id) throws Exception{ 327 waitForFocus(id, false); 328 } 329 330 }