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 javafx.scene.control; 27 28 import com.sun.javafx.scene.control.infrastructure.StageLoader; 29 import javafx.event.ActionEvent; 30 import javafx.event.EventHandler; 31 32 import javafx.geometry.Side; 33 import org.junit.After; 34 import org.junit.Before; 35 import org.junit.Test; 36 37 import static org.junit.Assert.*; 38 import static com.sun.javafx.scene.control.ContextMenuContentRetriever.*; 39 40 public class ContextMenuTest { 41 private MenuItem menuItem0, menuItem1, menuItem2, menuItem3; 42 43 private ContextMenu contextMenu; 44 private ContextMenu contextMenuWithOneItem; 45 private ContextMenu contextMenuWithManyItems; 46 47 private StageLoader sl; 48 private Button anchorBtn; 49 50 @Before public void setup() { 51 // earlier test items 52 menuItem0 = new MenuItem(); 53 menuItem1 = new MenuItem(); 54 menuItem2 = new MenuItem(); 55 menuItem3 = new MenuItem(); 56 57 contextMenu = new ContextMenu(); 58 contextMenuWithOneItem = new ContextMenu(menuItem0); 59 contextMenuWithManyItems = new ContextMenu(menuItem1, menuItem2, menuItem3); 60 61 62 // more recent test items 63 // specify items (layout relates to the item positioning inside menus) 64 menuItem = new MenuItem("MenuItem 1"); 65 subMenu = new Menu("submenu"); 66 subMenuItem1 = new MenuItem("SubMenuItem 1"); 67 customMenuItem = new CustomMenuItem(new Label("CustomMenuItem 1")); 68 69 // install items into menus 70 subMenu.getItems().setAll(subMenuItem1, customMenuItem); 71 72 // for the show/hide tests, we need a stage with an anchor in it 73 anchorBtn = new Button("Anchor"); 74 sl = new StageLoader(anchorBtn); 75 } 76 77 @After public void after() { 78 sl.dispose(); 79 } 80 81 @Test public void defaultGetId() { 82 assertNull(contextMenu.getId()); 83 } 84 85 @Test public void getStyleClassNotNull() { 86 assertNotNull(contextMenu.getStyleClass()); 87 } 88 89 @Test public void shouldBeAutoHideOn() { 90 assertTrue(contextMenu.isAutoHide()); 91 } 92 93 @Test public void shouldHaveZeroItems() { 94 assertEquals(0, contextMenu.getItems().size()); 95 } 96 97 @Test public void shouldHaveOneItem() { 98 assertEquals(1, contextMenuWithOneItem.getItems().size()); 99 } 100 101 @Test public void shouldHaveManyItems() { 102 assertEquals(3, contextMenuWithManyItems.getItems().size()); 103 } 104 105 @Test public void getDefaultSetOnActionHandler() { 106 assertNull(contextMenu.getOnAction()); 107 } 108 109 @Test public void getSpecifiedSetOnActionHandler() { 110 EventHandlerStub handler = new EventHandlerStub(); 111 contextMenu.setOnAction(handler); 112 assertEquals(handler, contextMenu.getOnAction()); 113 } 114 115 @Test public void setTwiceAndGetSpecifiedSetOnActionHandler() { 116 EventHandlerStub handler1 = new EventHandlerStub(); 117 EventHandlerStub handler2 = new EventHandlerStub(); 118 contextMenu.setOnAction(handler1); 119 contextMenu.setOnAction(handler2); 120 assertEquals(handler2, contextMenu.getOnAction()); 121 } 122 123 @Test public void getNullSetOnActionHandler() { 124 contextMenu.setOnAction(null); 125 assertNull(contextMenu.getOnAction()); 126 } 127 128 @Test public void defaultOnActionPropertyNotNull() { 129 assertNotNull(contextMenu.onActionProperty()); 130 } 131 132 @Test public void getOnActionPropertyBean() { 133 assertEquals(contextMenu, contextMenu.onActionProperty().getBean()); 134 } 135 136 @Test public void getOnActionPropertyName() { 137 assertEquals("onAction", contextMenu.onActionProperty().getName()); 138 } 139 140 @Test public void removedItemsAreChanged() { 141 contextMenuWithManyItems.getItems().remove(menuItem2); 142 assertNull(menuItem2.getParentPopup()); 143 } 144 145 @Test public void addedItemsAreChanged() { 146 MenuItem addedMenuItem = new MenuItem(); 147 contextMenuWithManyItems.getItems().add(addedMenuItem); 148 assertEquals(contextMenuWithManyItems, addedMenuItem.getParentPopup()); 149 } 150 151 @Test public void test_rt_34106_menus_should_not_be_reused() { 152 // This test ensures the new behavior of ContextMenu's whereby it is only 153 // allowed for a Menu/MenuItem to be in one parentPopup at a time. 154 // Previously we allowed multiple ContextMenus to refer to the same 155 // Menu/MenuItem, but this didn't work as there was no way to discern 156 // when to show 157 // 158 MenuItem item1 = new MenuItem("MenuItem 1"); 159 Menu menu = new Menu("Menu"); 160 menu.getItems().addAll(item1); 161 162 ContextMenu cm1 = new ContextMenu(menu); 163 assertEquals(1, cm1.getItems().size()); 164 assertEquals(menu, cm1.getItems().get(0)); 165 assertEquals(cm1, menu.getParentPopup()); 166 assertEquals(cm1, item1.getParentPopup()); 167 168 ContextMenu cm2 = new ContextMenu(menu); 169 assertEquals(0, cm1.getItems().size()); 170 assertEquals(1, cm2.getItems().size()); 171 assertEquals(menu, cm2.getItems().get(0)); 172 assertEquals(cm2, menu.getParentPopup()); 173 assertEquals(cm2, item1.getParentPopup()); 174 } 175 176 public static final class EventHandlerStub implements EventHandler<ActionEvent> { 177 boolean called = false; 178 @Override public void handle(ActionEvent event) { 179 called = true; 180 } 181 }; 182 183 184 185 186 187 private MenuItem menuItem; 188 private Menu subMenu; 189 private MenuItem subMenuItem1; 190 private CustomMenuItem customMenuItem; 191 192 private ContextMenu createContextMenu(boolean showMenu) { 193 // create and return the context menu with the root menu in it 194 ContextMenu contextMenu = new ContextMenu(menuItem, subMenu); 195 196 if (showMenu) { 197 contextMenu.show(anchorBtn, Side.RIGHT, 0, 0); 198 } 199 return contextMenu; 200 } 201 202 private void showMenu(ContextMenu cm, MenuItem... browseTo) { 203 cm.show(anchorBtn, Side.RIGHT, 0, 0); 204 205 if (browseTo == null) return; 206 207 // navigate through the browseTo list, focusing / expanding as necessary 208 for (int i = 0; i < browseTo.length; i++) { 209 MenuItem item = browseTo[i]; 210 211 // find item in current showing menu 212 boolean found = false; 213 while (true) { 214 MenuItem focusedItem = getCurrentFocusedItem(cm); 215 if (item == focusedItem) { 216 found = true; 217 break; 218 } 219 220 pressDownKey(cm); 221 } 222 223 if (! found) { 224 break; 225 } else { 226 if (item instanceof Menu) { 227 pressRightKey(cm); 228 } 229 } 230 } 231 } 232 233 private ContextMenu createContextMenuAndShowSubMenu() { 234 ContextMenu cm = createContextMenu(true); 235 236 // press down twice to go to subMenu 237 pressDownKey(cm); 238 pressDownKey(cm); 239 240 // ensure the submenu isn't showing (it should only show on right-arrow) 241 assertFalse(subMenu.isShowing()); 242 243 // open up the submenu 244 pressRightKey(cm); 245 assertTrue(subMenu.isShowing()); 246 assertEquals(subMenu, getShowingSubMenu(cm)); 247 248 // make sure the first item of the subMenu is focused 249 MenuItem focusedItem = getCurrentFocusedItem(cm); 250 assertEquals(0, getCurrentFocusedIndex(cm)); 251 assertEquals("Expected " + subMenuItem1.getText() + ", found " + focusedItem.getText(), 252 subMenuItem1, focusedItem); 253 254 return cm; 255 } 256 257 @Test public void test_showAndHide() { 258 ContextMenu cm = createContextMenu(false); 259 assertFalse(cm.isShowing()); 260 261 cm.show(anchorBtn, Side.RIGHT, 0, 0); 262 assertTrue(cm.isShowing()); 263 264 cm.hide(); 265 assertFalse(cm.isShowing()); 266 } 267 268 @Test public void test_navigateMenu_downwards() { 269 ContextMenu cm = createContextMenu(true); 270 271 assertNotNull(getShowingMenuContent(cm)); 272 assertEquals(-1, getCurrentFocusedIndex(cm)); 273 274 // press down once to go to menuItem 275 pressDownKey(cm); 276 MenuItem focusedItem = getCurrentFocusedItem(cm); 277 assertEquals(0, getCurrentFocusedIndex(cm)); 278 assertEquals("Expected " + menuItem.getText() + ", found " + focusedItem.getText(), 279 menuItem, focusedItem); 280 281 // press down once to go to subMenu 282 pressDownKey(cm); 283 focusedItem = getCurrentFocusedItem(cm); 284 assertEquals(1, getCurrentFocusedIndex(cm)); 285 assertEquals("Expected " + subMenu.getText() + ", found " + focusedItem.getText(), 286 subMenu, focusedItem); 287 288 // ensure the submenu isn't showing (it should only show on right-arrow) 289 assertFalse(subMenu.isShowing()); 290 291 // press down once more to loop back to the top (menuItem) 292 pressDownKey(cm); 293 focusedItem = getCurrentFocusedItem(cm); 294 assertEquals(0, getCurrentFocusedIndex(cm)); 295 assertEquals("Expected " + menuItem.getText() + ", found " + focusedItem.getText(), 296 menuItem, focusedItem); 297 } 298 299 @Test public void test_navigateMenu_upwards() { 300 ContextMenu cm = createContextMenu(true); 301 302 assertNotNull(getShowingMenuContent(cm)); 303 assertEquals(-1, getCurrentFocusedIndex(cm)); 304 305 // press up once to loop to the last menu item (subMenu) 306 pressUpKey(cm); 307 MenuItem focusedItem = getCurrentFocusedItem(cm); 308 assertEquals(1, getCurrentFocusedIndex(cm)); 309 assertEquals("Expected " + subMenu.getText() + ", found " + focusedItem.getText(), 310 subMenu, focusedItem); 311 312 // press up once to go to menuItem 313 pressDownKey(cm); 314 focusedItem = getCurrentFocusedItem(cm); 315 assertEquals(0, getCurrentFocusedIndex(cm)); 316 assertEquals("Expected " + menuItem.getText() + ", found " + focusedItem.getText(), 317 menuItem, focusedItem); 318 } 319 320 @Test public void test_navigateMenu_showSubMenu() { 321 createContextMenuAndShowSubMenu(); 322 } 323 324 @Test public void test_navigateSubMenu_downwards() { 325 ContextMenu cm = createContextMenuAndShowSubMenu(); 326 327 // we now have focus in the submenu, and on its first item, so lets navigate it 328 assertNotNull(getShowingMenuContent(cm)); 329 MenuItem focusedItem = getCurrentFocusedItem(cm); 330 assertEquals(0, getCurrentFocusedIndex(cm)); 331 assertEquals("Expected " + subMenuItem1.getText() + ", found " + focusedItem.getText(), 332 subMenuItem1, focusedItem); 333 334 // press down once to go to customMenuItem 335 pressDownKey(cm); 336 focusedItem = getCurrentFocusedItem(cm); 337 assertEquals(1, getCurrentFocusedIndex(cm)); 338 assertEquals("Expected " + customMenuItem.getText() + ", found " + focusedItem.getText(), 339 customMenuItem, focusedItem); 340 341 // press down once to go to wrap back around to subMenuItem1 342 pressDownKey(cm); 343 focusedItem = getCurrentFocusedItem(cm); 344 assertEquals(0, getCurrentFocusedIndex(cm)); 345 assertEquals("Expected " + subMenuItem1.getText() + ", found " + focusedItem.getText(), 346 subMenuItem1, focusedItem); 347 } 348 349 @Test public void test_navigateSubMenu_upwards() { 350 ContextMenu cm = createContextMenuAndShowSubMenu(); 351 352 // we now have focus in the submenu, and on its first item, so lets navigate it 353 assertNotNull(getShowingMenuContent(cm)); 354 MenuItem focusedItem = getCurrentFocusedItem(cm); 355 assertEquals(0, getCurrentFocusedIndex(cm)); 356 assertEquals("Expected " + subMenuItem1.getText() + ", found " + focusedItem.getText(), 357 subMenuItem1, focusedItem); 358 359 // press up once to go to customMenuItem 360 pressUpKey(cm); 361 focusedItem = getCurrentFocusedItem(cm); 362 assertEquals(1, getCurrentFocusedIndex(cm)); 363 assertEquals("Expected " + customMenuItem.getText() + ", found " + focusedItem.getText(), 364 customMenuItem, focusedItem); 365 366 // press up once to go to subMenuItem1 367 pressUpKey(cm); 368 focusedItem = getCurrentFocusedItem(cm); 369 assertEquals(0, getCurrentFocusedIndex(cm)); 370 assertEquals("Expected " + subMenuItem1.getText() + ", found " + focusedItem.getText(), 371 subMenuItem1, focusedItem); 372 } 373 374 @Test public void test_navigateSubMenu_rightKeyDoesNothing() { 375 ContextMenu cm = createContextMenuAndShowSubMenu(); 376 377 // we now have focus in the submenu, and on its first item, so lets navigate it 378 pressRightKey(cm); 379 assertNotNull(getShowingMenuContent(cm)); 380 MenuItem focusedItem = getCurrentFocusedItem(cm); 381 assertEquals(0, getCurrentFocusedIndex(cm)); 382 assertEquals("Expected " + subMenuItem1.getText() + ", found " + focusedItem.getText(), 383 subMenuItem1, focusedItem); 384 } 385 386 @Test public void test_navigateSubMenu_leftKeyClosesSubMenu() { 387 ContextMenu cm = createContextMenuAndShowSubMenu(); 388 389 // we now have focus in the submenu, and on its first item. 390 // If we press left we expect the submenu to close and for focus to go 391 // back to the parent menu. 392 pressLeftKey(cm); 393 assertNotNull(getShowingMenuContent(cm)); 394 MenuItem focusedItem = getCurrentFocusedItem(cm); 395 assertEquals(1, getCurrentFocusedIndex(cm)); 396 assertEquals("Expected " + subMenu.getText() + ", found " + focusedItem.getText(), 397 subMenu, focusedItem); 398 } 399 400 private int rt_37127_count = 0; 401 @Test public void test_rt_37127_keyboard() { 402 ContextMenu cm = createContextMenuAndShowSubMenu(); 403 404 customMenuItem.setOnAction(event -> rt_37127_count++); 405 406 // we now have focus in the submenu, and on its first item. 407 // For this test we now need to move focus down to the custom menu item 408 // and press the enter key. We expect to only receive one event 409 pressDownKey(cm); 410 MenuItem focusedItem = getCurrentFocusedItem(cm); 411 assertEquals(1, getCurrentFocusedIndex(cm)); 412 assertEquals(customMenuItem, focusedItem); 413 414 assertEquals(0, rt_37127_count); 415 pressEnterKey(cm); 416 assertEquals(1, rt_37127_count); 417 418 // now go back to the customMenuItem and press it again 419 showMenu(cm, subMenu, customMenuItem); 420 pressEnterKey(cm); 421 assertEquals(2, rt_37127_count); 422 423 // and once more.... 424 showMenu(cm, subMenu, customMenuItem); 425 pressEnterKey(cm); 426 assertEquals(3, rt_37127_count); 427 } 428 429 @Test public void test_rt_37127_mouse() { 430 ContextMenu cm = createContextMenuAndShowSubMenu(); 431 432 customMenuItem.setOnAction(event -> rt_37127_count++); 433 434 // we now have focus in the submenu, and on its first item. 435 // For this test we now need to move focus down to the custom menu item 436 // and press the enter key. We expect to only receive one event 437 pressDownKey(cm); 438 MenuItem focusedItem = getCurrentFocusedItem(cm); 439 assertEquals(1, getCurrentFocusedIndex(cm)); 440 assertEquals(customMenuItem, focusedItem); 441 442 assertEquals(0, rt_37127_count); 443 pressMouseButton(cm); 444 assertEquals(1, rt_37127_count); 445 446 // now go back to the customMenuItem and press it again 447 showMenu(cm, subMenu, customMenuItem); 448 pressMouseButton(cm); 449 assertEquals(2, rt_37127_count); 450 451 // and once more.... 452 showMenu(cm, subMenu, customMenuItem); 453 pressMouseButton(cm); 454 assertEquals(3, rt_37127_count); 455 } 456 457 @Test public void test_rt_37102() { 458 // This resulted in a NPE before the bug was fixed 459 ContextMenu cm = createContextMenuAndShowSubMenu(); 460 pressLeftKey(cm); 461 showMenu(cm, subMenu); 462 } 463 464 @Test public void test_rt_37091() { 465 ContextMenu cm = createContextMenuAndShowSubMenu(); 466 467 assertEquals(subMenu, getShowingSubMenu(cm)); 468 assertEquals(subMenu, getOpenSubMenu(cm)); 469 470 cm.hide(); 471 assertNull(getOpenSubMenu(cm)); 472 473 cm.getItems().clear(); 474 cm.getItems().add(subMenu); 475 476 assertNull(getOpenSubMenu(cm)); 477 cm.show(anchorBtn, Side.RIGHT, 0, 0); 478 pressDownKey(cm); 479 pressDownKey(cm); 480 pressRightKey(cm); 481 assertEquals(subMenu, getShowingSubMenu(cm)); 482 assertEquals(subMenuItem1, getCurrentFocusedItem(cm)); 483 } 484 }