1 /*
   2  * Copyright (c) 2018, 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 package test.robot.javafx.scene;
  26 
  27 import com.sun.glass.ui.Robot;
  28 import javafx.application.Application;
  29 import javafx.application.Platform;
  30 import javafx.scene.Scene;
  31 import javafx.scene.control.ContextMenu;
  32 import javafx.scene.control.MenuItem;
  33 import javafx.scene.control.Tab;
  34 import javafx.scene.control.TabPane;
  35 import javafx.stage.Stage;
  36 import javafx.stage.StageStyle;
  37 import javafx.stage.WindowEvent;
  38 
  39 import java.util.concurrent.CountDownLatch;
  40 import java.util.concurrent.TimeUnit;
  41 
  42 import org.junit.AfterClass;
  43 import org.junit.Assert;
  44 import org.junit.BeforeClass;
  45 import org.junit.Test;
  46 import static org.junit.Assert.fail;
  47 
  48 public class TabContextMenuCloseButtonTest {
  49 
  50     static CountDownLatch startupLatch = new CountDownLatch(1);
  51     static CountDownLatch cmlatch;
  52     static Robot robot;
  53     static TabPane tabPane;
  54     static volatile Stage stage;
  55     static volatile Scene scene;
  56     static ContextMenu cMenu;
  57     static boolean cmShown = false;
  58     static final int SCENE_WIDTH = 250;
  59     static final int SCENE_HEIGHT = SCENE_WIDTH;
  60     final int DX = 17;
  61     final int DY = DX;
  62 
  63     // Test close button with three mouse buttons.
  64     // Pressing left & middle button closes the tab.
  65     // Pressing right button does not close the tab.
  66     @Test
  67     public void testCloseButton() {
  68         int mouseButtons[] = { Robot.MOUSE_MIDDLE_BTN, Robot.MOUSE_RIGHT_BTN, Robot.MOUSE_LEFT_BTN };
  69         int expectedTabCount[] = {2, 2, 1};
  70         try {
  71             Thread.sleep(1000); // Wait for tabPane to layout
  72         } catch (Exception ex) {
  73             System.out.println("Thread was interrupted." + ex);
  74         }
  75         for (int i = 0; i < mouseButtons.length; ++i) {
  76             final int ic = i;
  77             CountDownLatch pressLatch = new CountDownLatch(1);
  78             Platform.runLater(() -> {
  79                 if (cMenu.isShowing()) {
  80                     cMenu.hide();
  81                 }
  82                 robot.mouseMove((int) (scene.getWindow().getX() + scene.getX() + DX),
  83                         (int) (scene.getWindow().getY() + scene.getY() + DY));
  84                 robot.mousePress(mouseButtons[ic]);
  85                 robot.mouseRelease(mouseButtons[ic]);
  86                 pressLatch.countDown();
  87             });
  88             waitForLatch(pressLatch, 5, "Timeout waiting for Robot.Button." + mouseButtons[i]);
  89             try {
  90                 Thread.sleep(500); // Wait for tabPane to layout
  91             } catch (Exception ex) {
  92                 System.out.println("Thread was interrupted." + ex);
  93             }
  94             Assert.assertEquals(expectedTabCount[i], tabPane.getTabs().size());
  95         }
  96     }
  97 
  98     // Test that pressing right mouse button shows context menu.
  99     @Test
 100     public void testContextMenu() {
 101         try {
 102             Thread.sleep(1000); // Wait for tabPane to layout
 103         } catch (Exception ex) {
 104             System.out.println("Thread was interrupted." + ex);
 105         }
 106         cmShown = false;
 107         CountDownLatch pressLatch = new CountDownLatch(1);
 108         cmlatch = new CountDownLatch(1);
 109         cMenu.setOnShown(event -> {
 110             cmShown = true;
 111             cmlatch.countDown();
 112         });
 113         Platform.runLater(() -> {
 114             if (cMenu.isShowing()) {
 115                 cMenu.hide();
 116             }
 117             robot.mouseMove((int)(scene.getWindow().getX() + scene.getX() + DX),
 118                     (int)(scene.getWindow().getY() + scene.getY() + DY));
 119             robot.mousePress(Robot.MOUSE_RIGHT_BTN);
 120             robot.mouseRelease(Robot.MOUSE_RIGHT_BTN);
 121             pressLatch.countDown();
 122         });
 123         waitForLatch(pressLatch, 5, "Timeout waiting for Robot.MOUSE_RIGHT_BTN.");
 124         waitForLatch(cmlatch, 5, "Timeout waiting for ContextMenu to be shown.");
 125         Assert.assertTrue("Tab's ContextMenu not shown.", cmShown);
 126     }
 127 
 128     public static void setupUI() {
 129         CountDownLatch latch = new CountDownLatch(1);
 130         Platform.runLater(() -> {
 131             cMenu = new ContextMenu(new MenuItem("hello"), new MenuItem("goodbye"));
 132 
 133             for (int i = 0; i < 3; ++i) {
 134                 Tab tab = new Tab("");
 135                 tab.setContextMenu(cMenu);
 136                 tabPane.getTabs().add(tab);
 137             }
 138             latch.countDown();
 139         });
 140         waitForLatch(latch, 5, "Timeout waiting for setupTest().");
 141     }
 142 
 143     @BeforeClass
 144     public static void initFX() {
 145         startupLatch = new CountDownLatch(1);
 146         new Thread(() -> Application.launch(TestApp.class, (String[])null)).start();
 147         waitForLatch(startupLatch, 10, "Timeout waiting for FX runtime to start");
 148         setupUI();
 149     }
 150 
 151     @AfterClass
 152     public static void exit() {
 153         Platform.runLater(() -> {
 154             tabPane.getTabs().removeAll();
 155             stage.hide();
 156         });
 157         Platform.exit();
 158     }
 159 
 160     public static class TestApp extends Application {
 161         @Override
 162         public void start(Stage primaryStage) {
 163             robot = com.sun.glass.ui.Application.GetApplication().createRobot();
 164             stage = primaryStage;
 165             tabPane = new TabPane();
 166             tabPane.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
 167             tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.ALL_TABS);
 168             scene = new Scene(tabPane, SCENE_WIDTH, SCENE_HEIGHT);
 169             stage.setScene(scene);
 170             stage.initStyle(StageStyle.UNDECORATED);
 171             stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e ->
 172                     Platform.runLater(startupLatch::countDown));
 173             stage.setAlwaysOnTop(true);
 174             stage.show();
 175         }
 176     }
 177 
 178     public static void waitForLatch(CountDownLatch latch, int seconds, String msg) {
 179         try {
 180             if (!latch.await(seconds, TimeUnit.SECONDS)) {
 181                 fail(msg);
 182             }
 183         } catch (Exception ex) {
 184             fail("Unexpected exception: " + ex);
 185         }
 186     }
 187 }