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 }