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