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 }