1 /* 2 * Copyright (c) 2010, 2015, 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 test.javafx.scene; 27 28 29 import test.com.sun.javafx.pgstub.StubScene; 30 import test.com.sun.javafx.pgstub.StubToolkit; 31 import com.sun.javafx.tk.Toolkit; 32 import static org.junit.Assert.assertEquals; 33 import static org.junit.Assert.assertFalse; 34 import static org.junit.Assert.assertNull; 35 import static org.junit.Assert.assertSame; 36 import static org.junit.Assert.assertTrue; 37 import static org.junit.Assert.fail; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 import javafx.beans.InvalidationListener; 42 import javafx.beans.Observable; 43 import javafx.beans.value.ChangeListener; 44 import javafx.beans.value.ObservableValue; 45 import javafx.scene.Group; 46 import javafx.scene.Node; 47 import javafx.scene.ParentShim; 48 import javafx.scene.Scene; 49 import javafx.scene.SceneShim; 50 51 import javafx.scene.shape.Rectangle; 52 import javafx.stage.Stage; 53 54 import org.junit.After; 55 import org.junit.Before; 56 import org.junit.Test; 57 58 public class FocusTest { 59 60 private Stage stage; 61 private Scene scene; 62 private List<Node> nodes; 63 private int nodeIndex; 64 private StubToolkit toolkit; 65 private boolean actionTaken; 66 67 @Before 68 public void setUp() { 69 stage = new Stage(); 70 scene = new Scene(new Group(), 500, 500); 71 stage.setScene(scene); 72 stage.show(); 73 stage.requestFocus(); 74 nodes = new ArrayList(); 75 nodeIndex = 0; 76 77 toolkit = (StubToolkit) Toolkit.getToolkit(); 78 } 79 80 @After 81 public void tearDown() { 82 stage.hide(); 83 stage = null; 84 scene = null; 85 } 86 87 void fireTestPulse() { 88 // TODO: an actual pulse doesn't work in the stub environment. 89 // Just clean up focus instead. 90 SceneShim.focusCleanup(scene); 91 } 92 93 boolean T = true; 94 boolean F = false; 95 96 Node n(boolean trav, boolean vis, boolean enable) { 97 Rectangle node = new Rectangle(); 98 node.setId("Rect-" + nodeIndex); 99 node.setFocusTraversable(trav); 100 node.setVisible(vis); 101 node.setDisable(!enable); 102 nodes.add(node); 103 nodeIndex++; 104 105 return node; 106 } 107 108 Node n() { 109 return n(T, T, T); 110 } 111 112 private void assertIsFocused(Scene s, Node n) { 113 assertEquals(n, s.getFocusOwner()); 114 assertTrue(n.isFocused()); 115 } 116 117 private void assertNotFocused(Scene s, Node n) { 118 assertTrue(n != s.getFocusOwner()); 119 assertFalse(n.isFocused()); 120 } 121 122 private void assertNullFocus(Scene s) { 123 assertNull(s.getFocusOwner()); 124 } 125 126 /** 127 * Test setting of initial focus. 128 */ 129 @Test 130 public void testInitial() { 131 assertNullFocus(scene); 132 scene.setRoot(new Group()); 133 ParentShim.getChildren(scene.getRoot()).add(n()); 134 fireTestPulse(); 135 assertIsFocused(scene, nodes.get(0)); 136 } 137 138 /** 139 * Test requestFocus() on eligible and ineligible nodes. 140 */ 141 @Test 142 public void testRequest() { 143 scene.setRoot(new Group()); 144 ParentShim.getChildren(scene.getRoot()).addAll( 145 n(T, T, T), // 0 - focusable 146 n(F, T, T), // 1 - focusable 147 n(T, T, F), // 2 - not focusable 148 n(F, T, F), // 3 - not focusable 149 n(T, F, T), // 4 - not focusable 150 n(F, F, T), // 5 - not focusable 151 n(T, F, F), // 6 - not focusable 152 n(F, F, F) // 7 - not focusable 153 ); 154 n(); // 8 - not focusable, because not in scene 155 156 nodes.get(0).requestFocus(); 157 assertIsFocused(scene, nodes.get(0)); 158 nodes.get(1).requestFocus(); 159 assertIsFocused(scene, nodes.get(1)); 160 nodes.get(2).requestFocus(); 161 assertNotFocused(scene, nodes.get(2)); 162 nodes.get(3).requestFocus(); 163 assertNotFocused(scene, nodes.get(3)); 164 nodes.get(4).requestFocus(); 165 assertNotFocused(scene, nodes.get(4)); 166 nodes.get(5).requestFocus(); 167 assertNotFocused(scene, nodes.get(5)); 168 nodes.get(6).requestFocus(); 169 assertNotFocused(scene, nodes.get(6)); 170 nodes.get(7).requestFocus(); 171 assertNotFocused(scene, nodes.get(7)); 172 nodes.get(8).requestFocus(); 173 assertNotFocused(scene, nodes.get(8)); 174 } 175 176 /** 177 * Test removing the focus owner without another eligible node in the scene. 178 */ 179 @Test 180 public void testRemove1() { 181 scene.setRoot(new Group()); 182 ParentShim.getChildren(scene.getRoot()).add(n()); 183 nodes.get(0).requestFocus(); 184 ParentShim.getChildren(scene.getRoot()).remove(nodes.get(0)); 185 fireTestPulse(); 186 assertNotFocused(scene, nodes.get(0)); 187 assertNullFocus(scene); 188 } 189 190 /** 191 * Test removing the focus owner with another eligible node in the scene. 192 */ 193 @Test 194 public void testRemove2() { 195 scene.setRoot(new Group()); 196 ParentShim.getChildren(scene.getRoot()).addAll(n(), n()); 197 nodes.get(0).requestFocus(); 198 ParentShim.getChildren(scene.getRoot()).remove(nodes.get(0)); 199 fireTestPulse(); 200 assertNotFocused(scene, nodes.get(0)); 201 assertIsFocused(scene, nodes.get(1)); 202 } 203 204 /** 205 * Test removing the focus owner without another eligible node in the scene. 206 */ 207 @Test 208 public void testRemove_ClearsFocusOnRemovedNode1() { 209 Node n = n(); 210 scene.setRoot(new Group()); 211 ParentShim.getChildren(scene.getRoot()).add(n); 212 n.requestFocus(); 213 ParentShim.getChildren(scene.getRoot()).remove(n); 214 fireTestPulse(); 215 assertNotFocused(scene, n); 216 } 217 218 /** 219 * Test removing the focus owner with another eligible node in the scene. 220 */ 221 @Test 222 public void testRemove_ClearsFocusOnRemovedNode2() { 223 Node n = n(); 224 scene.setRoot(new Group()); 225 ParentShim.getChildren(scene.getRoot()).addAll(n, n()); 226 n.requestFocus(); 227 ParentShim.getChildren(scene.getRoot()).remove(n); 228 fireTestPulse(); 229 assertNotFocused(scene, n); 230 assertIsFocused(scene, ParentShim.getChildren(scene.getRoot()).get(0)); 231 } 232 233 /** 234 * Test removing the focus owner without another eligible node in the scene. 235 */ 236 @Test 237 public void testRemoveChildOfGroup_ClearsFocusOnRemovedNode1() { 238 Node n = n(); 239 Group g = new Group(n); 240 scene.setRoot(new Group()); 241 ParentShim.getChildren(scene.getRoot()).add(g); 242 n.requestFocus(); 243 ParentShim.getChildren(g).remove(n); 244 fireTestPulse(); 245 assertNotFocused(scene, n); 246 } 247 248 /** 249 * Test making the focus owner invisible without another eligible 250 * node in the scene. 251 */ 252 @Test 253 public void testInvisible1() { 254 scene.setRoot(new Group()); 255 ParentShim.getChildren(scene.getRoot()).add(n()); 256 nodes.get(0).requestFocus(); 257 assertIsFocused(scene, nodes.get(0)); 258 nodes.get(0).setVisible(false); 259 fireTestPulse(); 260 assertNotFocused(scene, nodes.get(0)); 261 assertNullFocus(scene); 262 } 263 264 /** 265 * Test making the focus owner invisible with another eligible 266 * node in the scene. 267 */ 268 @Test 269 public void testInvisible2() { 270 scene.setRoot(new Group()); 271 ParentShim.getChildren(scene.getRoot()).addAll(n(), n()); 272 nodes.get(0).requestFocus(); 273 assertIsFocused(scene, nodes.get(0)); 274 nodes.get(0).setVisible(false); 275 fireTestPulse(); 276 assertNotFocused(scene, nodes.get(0)); 277 assertIsFocused(scene, nodes.get(1)); 278 } 279 280 /** 281 * Test making the focus owner disabled without another eligible 282 * node in the scene. 283 */ 284 @Test 285 public void testDisable1() { 286 scene.setRoot(new Group()); 287 ParentShim.getChildren(scene.getRoot()).add(n()); 288 nodes.get(0).requestFocus(); 289 nodes.get(0).setDisable(true); 290 fireTestPulse(); 291 assertNotFocused(scene, nodes.get(0)); 292 assertNullFocus(scene); 293 } 294 295 /** 296 * Test making the focus owner disabled with another eligible 297 * node in the scene. 298 */ 299 @Test 300 public void testDisable2() { 301 scene.setRoot(new Group()); 302 ParentShim.getChildren(scene.getRoot()).addAll(n(), n()); 303 nodes.get(0).requestFocus(); 304 nodes.get(0).setDisable(true); 305 fireTestPulse(); 306 assertNotFocused(scene, nodes.get(0)); 307 assertIsFocused(scene, nodes.get(1)); 308 } 309 310 /** 311 * When focus null, test adding an eligible, traversable node. 312 */ 313 @Test 314 public void testAddEligible() { 315 fireTestPulse(); // make sure focus is clean 316 assertNullFocus(scene); 317 scene.setRoot(new Group()); 318 ParentShim.getChildren(scene.getRoot()).add(n()); 319 fireTestPulse(); 320 assertIsFocused(scene, nodes.get(0)); 321 } 322 323 /** 324 * When focus null, test making a node traversable. 325 */ 326 @Test 327 public void testBecomeTraversable() { 328 scene.setRoot(new Group()); 329 ParentShim.getChildren(scene.getRoot()).addAll(n(F, T, T), n(F, T, T)); 330 fireTestPulse(); 331 assertNullFocus(scene); 332 toolkit.clearPulseRequested(); 333 assertFalse(toolkit.isPulseRequested()); 334 nodes.get(0).setFocusTraversable(true); 335 assertTrue(toolkit.isPulseRequested()); 336 fireTestPulse(); 337 assertIsFocused(scene, nodes.get(0)); 338 } 339 340 /** 341 * When focus null, test making a node visible. 342 */ 343 @Test 344 public void testBecomeVisible() { 345 scene.setRoot(new Group()); 346 ParentShim.getChildren(scene.getRoot()).add(n(T, F, T)); 347 fireTestPulse(); 348 assertNullFocus(scene); 349 nodes.get(0).setVisible(true); 350 fireTestPulse(); 351 assertIsFocused(scene, nodes.get(0)); 352 } 353 354 /** 355 * When focus null, test making a node enabled. 356 */ 357 @Test 358 public void testBecomeEnabled() { 359 scene.setRoot(new Group()); 360 ParentShim.getChildren(scene.getRoot()).add(n(T, T, F)); 361 fireTestPulse(); 362 assertNullFocus(scene); 363 nodes.get(0).setDisable(false); 364 fireTestPulse(); 365 assertIsFocused(scene, nodes.get(0)); 366 } 367 368 /** 369 * When focus exists, test adding an eligible, traversable node. 370 */ 371 @Test 372 public void testAddEligible2() { 373 scene.setRoot(new Group()); 374 ParentShim.getChildren(scene.getRoot()).add(n()); 375 fireTestPulse(); 376 assertIsFocused(scene, nodes.get(0)); 377 ParentShim.getChildren(scene.getRoot()).add(n()); 378 fireTestPulse(); 379 assertIsFocused(scene, nodes.get(0)); 380 } 381 382 /** 383 * When focus exists, test making a node traversable. 384 */ 385 @Test 386 public void testBecomeTraversable2() { 387 scene.setRoot(new Group()); 388 ParentShim.getChildren(scene.getRoot()).addAll(n(), n(F, T, T)); 389 fireTestPulse(); 390 assertIsFocused(scene, nodes.get(0)); 391 nodes.get(1).setFocusTraversable(true); 392 fireTestPulse(); 393 assertIsFocused(scene, nodes.get(0)); 394 } 395 396 /** 397 * When focus exists, test making a node visible. 398 */ 399 @Test 400 public void testBecomeVisible2() { 401 scene.setRoot(new Group()); 402 ParentShim.getChildren(scene.getRoot()).addAll(n(), n(T, F, T)); 403 fireTestPulse(); 404 assertIsFocused(scene, nodes.get(0)); 405 nodes.get(1).setVisible(true); 406 fireTestPulse(); 407 assertIsFocused(scene, nodes.get(0)); 408 } 409 410 /** 411 * When focus exists, test making a node enabled. 412 */ 413 @Test 414 public void testBecomeEnabled2() { 415 scene.setRoot(new Group()); 416 ParentShim.getChildren(scene.getRoot()).addAll(n(), n(T, T, F)); 417 fireTestPulse(); 418 assertIsFocused(scene, nodes.get(0)); 419 nodes.get(1).setDisable(false); 420 fireTestPulse(); 421 assertIsFocused(scene, nodes.get(0)); 422 } 423 424 /** 425 * Test moving the focus within a scene. 426 */ 427 @Test 428 public void testMoveWithinScene() { 429 Group g1 = new Group(n()); 430 Group g2 = new Group(); 431 scene.setRoot(new Group()); 432 ParentShim.getChildren(scene.getRoot()).addAll(g1, g2); 433 fireTestPulse(); 434 assertIsFocused(scene, nodes.get(0)); 435 ParentShim.getChildren(g2).add(nodes.get(0)); 436 fireTestPulse(); 437 assertIsFocused(scene, nodes.get(0)); 438 } 439 440 /** 441 * Test moving the focus into an invisible group, with no other 442 * eligible nodes in the scene. 443 */ 444 @Test 445 public void testMoveIntoInvisible() { 446 Group g1 = new Group(n()); 447 Group g2 = new Group(); 448 g2.setVisible(false); 449 scene.setRoot(new Group()); 450 ParentShim.getChildren(scene.getRoot()).addAll(g1, g2); 451 fireTestPulse(); 452 assertIsFocused(scene, nodes.get(0)); 453 ParentShim.getChildren(g2).add(nodes.get(0)); 454 fireTestPulse(); 455 assertNullFocus(scene); 456 } 457 458 /** 459 * Test moving the focus into an invisible group, with another 460 * eligible node in the scene. 461 */ 462 @Test 463 public void testMoveIntoInvisible2() { 464 Group g1 = new Group(n()); 465 Group g2 = new Group(); 466 g2.setVisible(false); 467 scene.setRoot(new Group()); 468 ParentShim.getChildren(scene.getRoot()).addAll(g1, g2, n()); 469 nodes.get(0).requestFocus(); 470 fireTestPulse(); 471 assertIsFocused(scene, nodes.get(0)); 472 ParentShim.getChildren(g2).add(nodes.get(0)); 473 fireTestPulse(); 474 assertIsFocused(scene, nodes.get(1)); 475 } 476 477 /** 478 * Test moving the focus into a disabled group, with no other 479 * eligible nodes in the scene. 480 */ 481 @Test 482 public void testMoveIntoDisabled() { 483 Group g1 = new Group(n()); 484 Group g2 = new Group(); 485 g2.setDisable(true); 486 scene.setRoot(new Group()); 487 ParentShim.getChildren(scene.getRoot()).addAll(g1, g2); 488 fireTestPulse(); 489 assertIsFocused(scene, nodes.get(0)); 490 ParentShim.getChildren(g2).add(nodes.get(0)); 491 fireTestPulse(); 492 assertNullFocus(scene); 493 } 494 495 /** 496 * Test moving the focus into a disabled group, with another 497 * eligible nodes in the scene. 498 */ 499 @Test 500 public void testMoveIntoDisabled2() { 501 Group g1 = new Group(n()); 502 Group g2 = new Group(); 503 g2.setDisable(true); 504 scene.setRoot(new Group()); 505 ParentShim.getChildren(scene.getRoot()).addAll(g1, g2, n()); 506 nodes.get(0).requestFocus(); 507 fireTestPulse(); 508 assertIsFocused(scene, nodes.get(0)); 509 ParentShim.getChildren(g2).add(nodes.get(0)); 510 fireTestPulse(); 511 assertIsFocused(scene, nodes.get(1)); 512 } 513 514 /** 515 * Test making the parent of focused node disabled, with no other 516 * eligible nodes in the scene. 517 */ 518 @Test 519 public void testMakeParentDisabled() { 520 Group g1 = new Group(n()); 521 Group g2 = new Group(); 522 g2.setVisible(false); 523 scene.setRoot(new Group()); 524 ParentShim.getChildren(scene.getRoot()).addAll(g1, g2); 525 fireTestPulse(); 526 assertIsFocused(scene, nodes.get(0)); 527 g1.setDisable(true); 528 fireTestPulse(); 529 assertNotFocused(scene, nodes.get(0)); 530 assertNullFocus(scene); 531 } 532 533 /** 534 * Test making the parent of focused node disabled, with another 535 * eligible nodes in the scene. 536 */ 537 @Test 538 public void testMakeParentDisabled2() { 539 Group g1 = new Group(n()); 540 Group g2 = new Group(); 541 g2.setVisible(false); 542 scene.setRoot(new Group()); 543 ParentShim.getChildren(scene.getRoot()).addAll(g1, g2, n()); 544 nodes.get(0).requestFocus(); 545 fireTestPulse(); 546 assertIsFocused(scene, nodes.get(0)); 547 g1.setDisable(true); 548 fireTestPulse(); 549 assertNotFocused(scene, nodes.get(0)); 550 assertIsFocused(scene, nodes.get(1)); 551 } 552 553 /** 554 * Test making the parent of focused node invisible, with no other 555 * eligible nodes in the scene. 556 */ 557 @Test 558 public void testMakeParentInvisible() { 559 Group g1 = new Group(n()); 560 Group g2 = new Group(); 561 g2.setVisible(false); 562 scene.setRoot(new Group()); 563 ParentShim.getChildren(scene.getRoot()).addAll(g1, g2); 564 fireTestPulse(); 565 assertIsFocused(scene, nodes.get(0)); 566 g1.setVisible(false); 567 fireTestPulse(); 568 assertNotFocused(scene, nodes.get(0)); 569 assertNullFocus(scene); 570 } 571 572 /** 573 * Test making the parent of focused node invisible, with another 574 * eligible nodes in the scene. 575 */ 576 @Test 577 public void testMakeParentInvisible2() { 578 Group g1 = new Group(n()); 579 Group g2 = new Group(); 580 g2.setVisible(false); 581 scene.setRoot(new Group()); 582 ParentShim.getChildren(scene.getRoot()).addAll(g1, g2, n()); 583 nodes.get(0).requestFocus(); 584 fireTestPulse(); 585 assertIsFocused(scene, nodes.get(0)); 586 g1.setVisible(false); 587 fireTestPulse(); 588 assertNotFocused(scene, nodes.get(0)); 589 assertIsFocused(scene, nodes.get(1)); 590 } 591 592 /** 593 * Focus should not move if stacking order changes. 594 */ 595 @Test 596 public void testToFront() { 597 scene.setRoot(new Group()); 598 ParentShim.getChildren(scene.getRoot()).addAll(n(), n()); 599 nodes.get(0).requestFocus(); 600 assertIsFocused(scene, nodes.get(0)); 601 assertNotFocused(scene, nodes.get(1)); 602 nodes.get(0).toFront(); 603 assertIsFocused(scene, nodes.get(0)); 604 assertNotFocused(scene, nodes.get(1)); 605 nodes.get(0).toBack(); 606 assertIsFocused(scene, nodes.get(0)); 607 assertNotFocused(scene, nodes.get(1)); 608 } 609 610 /** 611 * Test moving focused node into scene which is not in active stage. 612 */ 613 @Test 614 public void testMoveIntoInactiveScene() { 615 scene.setRoot(new Group()); 616 ParentShim.getChildren(scene.getRoot()).add(n()); 617 nodes.get(0).requestFocus(); 618 assertIsFocused(scene, nodes.get(0)); 619 Scene scene2 = new Scene(new Group()); 620 ParentShim.getChildren(scene2.getRoot()).add(nodes.get(0)); 621 fireTestPulse(); 622 assertNullFocus(scene); 623 assertNullFocus(scene2); 624 nodes.get(0).requestFocus(); 625 fireTestPulse(); 626 assertNullFocus(scene); 627 assertEquals(nodes.get(0), scene2.getFocusOwner()); 628 assertFalse(nodes.get(0).isFocused()); 629 stage.setScene(scene2); 630 fireTestPulse(); 631 assertNullFocus(scene); 632 assertIsFocused(scene2, nodes.get(0)); 633 } 634 635 /** 636 * Test making stage invisible. 637 */ 638 @Test 639 public void testInvisibleStage() { 640 scene.setRoot(new Group()); 641 ParentShim.getChildren(scene.getRoot()).add(n()); 642 nodes.get(0).requestFocus(); 643 stage.show(); 644 fireTestPulse(); 645 assertIsFocused(scene, nodes.get(0)); 646 stage.hide(); 647 nodes.get(0).requestFocus(); 648 fireTestPulse(); 649 assertEquals(nodes.get(0), scene.getFocusOwner()); 650 assertFalse(nodes.get(0).isFocused()); 651 } 652 653 /** 654 * Test switching two scenes in one stage. Focus owner in scenes should 655 * remain the same while focused node should change. 656 */ 657 @Test 658 public void testSwitchScenes(){ 659 scene.setRoot(new Group()); 660 ParentShim.getChildren(scene.getRoot()).add(n()); 661 nodes.get(0).requestFocus(); 662 Scene scene2 = new Scene(new Group()); 663 ParentShim.getChildren(scene2.getRoot()).add(n()); 664 nodes.get(1).requestFocus(); 665 fireTestPulse(); 666 assertIsFocused(scene, nodes.get(0)); 667 assertFalse(nodes.get(1).isFocused()); 668 assertEquals(nodes.get(1), scene2.getFocusOwner()); 669 stage.setScene(scene2); 670 fireTestPulse(); 671 assertFalse(nodes.get(0).isFocused()); 672 assertEquals(nodes.get(0), scene.getFocusOwner()); 673 assertIsFocused(scene2, nodes.get(1)); 674 } 675 676 @Test public void nestedFocusRequestsShouldResultInOneFocusedNode() { 677 final Node n1 = n(); 678 final Node n2 = n(); 679 scene.setRoot(new Group(n1, n2)); 680 681 n1.focusedProperty().addListener((ov, lostFocus, getFocus) -> { 682 if (lostFocus) { 683 n1.requestFocus(); 684 } 685 }); 686 687 n2.focusedProperty().addListener(o -> { 688 // n2 can be invalidated, but should not have focus 689 assertTrue(n1.isFocused()); 690 assertFalse(n2.isFocused()); 691 }); 692 693 n2.focusedProperty().addListener((ov, lostFocus, getFocus) -> fail("n2 should never get focus")); 694 695 stage.show(); 696 n1.requestFocus(); 697 assertTrue(n1.isFocused()); 698 assertFalse(n2.isFocused()); 699 700 n2.requestFocus(); 701 assertTrue(n1.isFocused()); 702 assertFalse(n2.isFocused()); 703 } 704 705 @Test public void shouldCancelInputMethodWhenLoosingFocus() { 706 final Node n1 = n(); 707 final Node n2 = n(); 708 scene.setRoot(new Group(n1, n2)); 709 710 stage.show(); 711 712 Toolkit.getToolkit().firePulse(); 713 714 n1.requestFocus(); 715 assertSame(n1, scene.getFocusOwner()); 716 actionTaken = false; 717 718 ((StubScene) scene.impl_getPeer()).setInputMethodCompositionFinishDelegate( 719 () -> { 720 assertSame(n1, scene.getFocusOwner()); 721 actionTaken = true; 722 } 723 ); 724 725 n2.requestFocus(); 726 727 ((StubScene) scene.impl_getPeer()).setInputMethodCompositionFinishDelegate( 728 null); 729 730 assertSame(n2, scene.getFocusOwner()); 731 assertTrue(actionTaken); 732 } 733 734 // TODO: tests for moving nodes between scenes 735 // and active and inactive stages 736 }