modules/graphics/src/main/java/javafx/scene/Scene.java

Print this page




 351         init(width, height);
 352         setFill(fill);
 353     }
 354 
 355     static {
 356             PerformanceTracker.setSceneAccessor(new PerformanceTracker.SceneAccessor() {
 357                 public void setPerfTracker(Scene scene, PerformanceTracker tracker) {
 358                     synchronized (trackerMonitor) {
 359                         scene.tracker = tracker;
 360                     }
 361                 }
 362                 public PerformanceTracker getPerfTracker(Scene scene) {
 363                     synchronized (trackerMonitor) {
 364                         return scene.tracker;
 365                     }
 366                 }
 367             });
 368             SceneHelper.setSceneAccessor(
 369                     new SceneHelper.SceneAccessor() {
 370                         @Override


















































 371                         public void setPaused(boolean paused) {
 372                             Scene.paused = paused;
 373                         }
 374 
 375                         @Override
 376                         public void parentEffectiveOrientationInvalidated(
 377                                 final Scene scene) {
 378                             scene.parentEffectiveOrientationInvalidated();
 379                         }
 380 
 381                         @Override
 382                         public Camera getEffectiveCamera(Scene scene) {
 383                             return scene.getEffectiveCamera();
 384                         }
 385 
 386                         @Override
 387                         public Scene createPopupScene(Parent root) {
 388                             return new Scene(root) {
 389                                        @Override
 390                                        void doLayoutPass() {


 420 
 421         // For debugging
 422         private static boolean inSynchronizer = false;
 423         private static boolean inMousePick = false;
 424         private static boolean allowPGAccess = false;
 425         private static int pgAccessCount = 0;
 426 
 427         // Flag set by the Toolkit when we are paused for JMX debugging
 428         private static boolean paused = false;
 429 
 430         /**
 431          * Used for debugging purposes. Returns true if we are in either the
 432          * mouse event code (picking) or the synchronizer, or if the scene is
 433          * not yet initialized,
 434          *
 435          */
 436         static boolean isPGAccessAllowed() {
 437             return inSynchronizer || inMousePick || allowPGAccess;
 438         }
 439 
 440         /**
 441          * @treatAsPrivate implementation detail
 442          * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 443          */
 444         @Deprecated
 445         public static void impl_setAllowPGAccess(boolean flag) {
 446             if (Utils.assertionEnabled()) {
 447                 if (flag) {
 448                     pgAccessCount++;
 449                     allowPGAccess = true;
 450                 }
 451                 else {
 452                     if (pgAccessCount <= 0) {
 453                         throw new java.lang.AssertionError("*** pgAccessCount underflow");
 454                     }
 455                     if (--pgAccessCount == 0) {
 456                         allowPGAccess = false;
 457                     }
 458                 }
 459             }
 460         }
 461 
 462         /**
 463          * If true, use the platform's drag gesture detection
 464          * else use Scene-level detection as per DnDGesture.process(MouseEvent, List)
 465          */


 527             // The dirty bit isn't checked but we must ensure it is cleared.
 528             // The cssFlag is set to clean in either Node.processCSS or
 529             // Node.impl_processCSS(boolean)
 530             sceneRoot.impl_clearDirty(com.sun.javafx.scene.DirtyBits.NODE_CSS);
 531             sceneRoot.processCSS();
 532         }
 533     }
 534 
 535     void doLayoutPass() {
 536         final Parent r = getRoot();
 537         if (r != null) {
 538             r.layout();
 539         }
 540     }
 541 
 542     /**
 543      * The peer of this scene
 544      */
 545     private TKScene peer;
 546 
 547     /**
 548      * Get Scene's peer
 549      *
 550      * @treatAsPrivate implementation detail
 551      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 552      */
 553     @Deprecated
 554     public TKScene impl_getPeer() {
 555         return peer;
 556     }
 557 
 558     /**
 559      * The scene pulse listener that gets called on toolkit pulses
 560      */
 561     ScenePulseListener scenePulseListener = new ScenePulseListener();
 562 
 563     /**
 564      * @treatAsPrivate implementation detail
 565      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 566      */
 567     @Deprecated
 568     public TKPulseListener impl_getScenePulseListener() {
 569         if (SystemProperties.isDebug()) {
 570             return scenePulseListener;
 571         }
 572         return null;
 573     }
 574 
 575     private List<Runnable> preLayoutPulseListeners;
 576     private List<Runnable> postLayoutPulseListeners;
 577 
 578     /**
 579      * Adds a new scene pre layout pulse listener to this scene. Every time a pulse occurs,
 580      * this listener will be called on the JavaFX Application Thread directly
 581      * <strong>before</strong> the CSS and layout passes, and also before
 582      * any rendering is done for
 583      * this frame. This scene pulse listener is suitable for knowing when a
 584      * scenegraph pulse is happening and also for modifying the scenegraph
 585      * (as it is called before CSS and layout, so any changes made will be properly
 586      * styled and positioned).
 587      *
 588      * This method must be called on the JavaFX Application thread.


 700      * and {@link javafx.scene.SceneAntialiasing SceneAntialiasing}
 701      * for more information.
 702      * @since JavaFX 8.0
 703      */
 704     public final SceneAntialiasing getAntiAliasing() {
 705         return antiAliasing;
 706     }
 707 
 708     private boolean getAntiAliasingInternal() {
 709         return (antiAliasing != null &&
 710                 Toolkit.getToolkit().isMSAASupported() &&
 711                 Platform.isSupported(ConditionalFeature.SCENE3D)) ?
 712                 antiAliasing != SceneAntialiasing.DISABLED : false;
 713     }
 714 
 715     /**
 716      * The {@code Window} for this {@code Scene}
 717      */
 718     private ReadOnlyObjectWrapper<Window> window;
 719 
 720     private void setWindow(Window value) {
 721         windowPropertyImpl().set(value);
 722     }
 723 
 724     public final Window getWindow() {
 725         return window == null ? null : window.get();
 726     }
 727 
 728     public final ReadOnlyObjectProperty<Window> windowProperty() {
 729         return windowPropertyImpl().getReadOnlyProperty();
 730     }
 731 
 732     private ReadOnlyObjectWrapper<Window> windowPropertyImpl() {
 733         if (window == null) {
 734             window = new ReadOnlyObjectWrapper<Window>() {
 735                 private Window oldWindow;
 736 
 737                 @Override protected void invalidated() {
 738                     final Window newWindow = get();
 739                     getKeyHandler().windowForSceneChanged(oldWindow, newWindow);
 740                     if (oldWindow != null) {
 741                         impl_disposePeer();
 742                     }
 743                     if (newWindow != null) {
 744                         impl_initPeer();
 745                     }
 746                     parentEffectiveOrientationInvalidated();
 747 
 748                     oldWindow = newWindow;
 749                 }
 750 
 751                 @Override
 752                 public Object getBean() {
 753                     return Scene.this;
 754                 }
 755 
 756                 @Override
 757                 public String getName() {
 758                     return "window";
 759                 }
 760             };
 761         }
 762         return window;
 763     }
 764 
 765     /**
 766      * @treatAsPrivate implementation detail
 767      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 768      */
 769     @Deprecated
 770     public void impl_setWindow(Window value) {
 771         setWindow(value);
 772     }
 773 
 774     /**
 775      * @treatAsPrivate implementation detail
 776      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 777      */
 778     @Deprecated
 779     public void impl_initPeer() {
 780         assert peer == null;
 781 
 782         Window window = getWindow();
 783         // impl_initPeer() is only called from Window, either when the window
 784         // is being shown, or the window scene is being changed. In any case
 785         // this scene's window cannot be null.
 786         assert window != null;
 787 
 788         TKStage windowPeer = WindowHelper.getPeer(window);
 789         if (windowPeer == null) {
 790             // This is fine, the window is not visible. impl_initPeer() will
 791             // be called again later, when the window is being shown.
 792             return;
 793         }
 794 
 795         final boolean isTransparentWindowsSupported = Platform.isSupported(ConditionalFeature.TRANSPARENT_WINDOW);
 796         if (!isTransparentWindowsSupported) {
 797             PlatformImpl.addNoTransparencyStylesheetToScene(this);
 798         }
 799 
 800         PerformanceTracker.logEvent("Scene.initPeer started");
 801 
 802         impl_setAllowPGAccess(true);
 803 
 804         Toolkit tk = Toolkit.getToolkit();
 805         peer = windowPeer.createTKScene(isDepthBufferInternal(), getAntiAliasingInternal(), acc);
 806         PerformanceTracker.logEvent("Scene.initPeer TKScene created");
 807         peer.setTKSceneListener(new ScenePeerListener());
 808         peer.setTKScenePaintListener(new ScenePeerPaintListener());
 809         PerformanceTracker.logEvent("Scene.initPeer TKScene set");
 810         peer.setRoot(getRoot().impl_getPeer());
 811         peer.setFillPaint(getFill() == null ? null : tk.getPaint(getFill()));
 812         getEffectiveCamera().impl_updatePeer();
 813         peer.setCamera((NGCamera) getEffectiveCamera().impl_getPeer());
 814         peer.markDirty();
 815         PerformanceTracker.logEvent("Scene.initPeer TKScene initialized");
 816 
 817         impl_setAllowPGAccess(false);
 818 
 819         tk.addSceneTkPulseListener(scenePulseListener);
 820         // listen to dnd gestures coming from the platform
 821         if (PLATFORM_DRAG_GESTURE_INITIATION) {
 822             if (dragGestureListener == null) {
 823                 dragGestureListener = new DragGestureListener();
 824             }
 825             tk.registerDragGestureListener(peer, EnumSet.allOf(TransferMode.class), dragGestureListener);
 826         }
 827         tk.enableDrop(peer, new DropTargetListener());
 828         tk.installInputMethodRequests(peer, new InputMethodRequestsDelegate());
 829 
 830         PerformanceTracker.logEvent("Scene.initPeer finished");
 831     }
 832 
 833     /**
 834      * @treatAsPrivate implementation detail
 835      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 836      */
 837     @Deprecated
 838     public void impl_disposePeer() {
 839         if (peer == null) {
 840             // This is fine, the window is either not shown yet and there is no
 841             // need in disposing scene peer, or is hidden and impl_disposePeer()
 842             // has already been called.
 843             return;
 844         }
 845 
 846         PerformanceTracker.logEvent("Scene.disposePeer started");
 847 
 848         Toolkit tk = Toolkit.getToolkit();
 849         tk.removeSceneTkPulseListener(scenePulseListener);
 850         if (accessible != null) {
 851             disposeAccessibles();
 852             Node root = getRoot();
 853             if (root != null) root.releaseAccessible();
 854             accessible.dispose();
 855             accessible = null;
 856         }
 857         peer.dispose();
 858         peer = null;


1282             double x, double y, double w, double h,
1283             Node root, BaseTransform transform, boolean depthBuffer,
1284             Paint fill, Camera camera, WritableImage wimg) {
1285 
1286         Toolkit tk = Toolkit.getToolkit();
1287         Toolkit.ImageRenderingContext context = new Toolkit.ImageRenderingContext();
1288 
1289         int xMin = (int)Math.floor(x);
1290         int yMin = (int)Math.floor(y);
1291         int xMax = (int)Math.ceil(x + w);
1292         int yMax = (int)Math.ceil(y + h);
1293         int width = Math.max(xMax - xMin, 1);
1294         int height = Math.max(yMax - yMin, 1);
1295         if (wimg == null) {
1296             wimg = new WritableImage(width, height);
1297         } else {
1298             width = (int)wimg.getWidth();
1299             height = (int)wimg.getHeight();
1300         }
1301 
1302         impl_setAllowPGAccess(true);
1303         context.x = xMin;
1304         context.y = yMin;
1305         context.width = width;
1306         context.height = height;
1307         context.transform = transform;
1308         context.depthBuffer = depthBuffer;
1309         context.root = root.impl_getPeer();
1310         context.platformPaint = fill == null ? null : tk.getPaint(fill);
1311         double cameraViewWidth = 1.0;
1312         double cameraViewHeight = 1.0;
1313         if (camera != null) {
1314             // temporarily adjust camera viewport to the snapshot size
1315             cameraViewWidth = camera.getViewWidth();
1316             cameraViewHeight = camera.getViewHeight();
1317             camera.setViewWidth(width);
1318             camera.setViewHeight(height);
1319             camera.impl_updatePeer();
1320             context.camera = camera.impl_getPeer();
1321         } else {
1322             context.camera = null;
1323         }
1324 
1325         // Grab the lights from the scene
1326         context.lights = null;
1327         if (scene != null && !scene.lights.isEmpty()) {
1328             context.lights = new NGLightBase[scene.lights.size()];
1329             for (int i = 0; i < scene.lights.size(); i++) {
1330                 context.lights[i] = scene.lights.get(i).impl_getPeer();
1331             }
1332         }
1333 
1334         Toolkit.WritableImageAccessor accessor = Toolkit.getWritableImageAccessor();
1335         context.platformImage = accessor.getTkImageLoader(wimg);
1336         impl_setAllowPGAccess(false);
1337         Object tkImage = tk.renderToImage(context);
1338         accessor.loadTkImage(wimg, tkImage);
1339 
1340         if (camera != null) {
1341             impl_setAllowPGAccess(true);
1342             camera.setViewWidth(cameraViewWidth);
1343             camera.setViewHeight(cameraViewHeight);
1344             camera.impl_updatePeer();
1345             impl_setAllowPGAccess(false);
1346         }
1347 
1348         // if this scene belongs to some stage
1349         // we need to mark the entire scene as dirty
1350         // because dirty logic is buggy
1351         if (scene != null && scene.peer != null) {
1352             scene.setNeedsRepaint();
1353         }
1354 
1355         return wimg;
1356     }
1357 
1358     /**
1359      * Implementation method for snapshot
1360      */
1361     private WritableImage doSnapshot(WritableImage img) {
1362         // TODO: no need to do CSS, layout or sync in the deferred case,
1363         // if this scene is attached to a visible stage
1364         doCSSLayoutSyncForSnapshot(getRoot());
1365 


1719         }
1720         if (height >= 0) {
1721             heightSetByUser = height;
1722             setHeight((float)height);
1723         }
1724         sizeInitialized = (widthSetByUser >= 0 && heightSetByUser >= 0);
1725     }
1726 
1727     private void init() {
1728         if (PerformanceTracker.isLoggingEnabled()) {
1729             PerformanceTracker.logEvent("Scene.init for [" + this + "]");
1730         }
1731         mouseHandler = new MouseHandler();
1732         clickGenerator = new ClickGenerator();
1733 
1734         if (PerformanceTracker.isLoggingEnabled()) {
1735             PerformanceTracker.logEvent("Scene.init for [" + this + "] - finished");
1736         }
1737     }
1738 
1739     private void preferredSize() {
1740         final Parent root = getRoot();
1741 
1742         // one or the other isn't initialized, need to perform layout in
1743         // order to ensure we can properly measure the preferred size of the
1744         // scene
1745         doCSSPass();
1746 
1747         resizeRootToPreferredSize(root);
1748         doLayoutPass();
1749 
1750         if (widthSetByUser < 0) {
1751             setWidth(root.isResizable()? root.getLayoutX() + root.getTranslateX() + root.getLayoutBounds().getWidth() :
1752                             root.getBoundsInParent().getMaxX());
1753         } else {
1754             setWidth(widthSetByUser);
1755         }
1756 
1757         if (heightSetByUser < 0) {
1758             setHeight(root.isResizable()? root.getLayoutY() + root.getTranslateY() + root.getLayoutBounds().getHeight() :
1759                             root.getBoundsInParent().getMaxY());


1796             return forcedWidth;
1797         }
1798         final double normalizedHeight = (height >= 0) ? height : -1;
1799         return root.boundedSize(root.prefWidth(normalizedHeight),
1800                                 root.minWidth(normalizedHeight),
1801                                 root.maxWidth(normalizedHeight));
1802     }
1803 
1804     private static double getPreferredHeight(Parent root,
1805                                              double forcedHeight,
1806                                              double width) {
1807         if (forcedHeight >= 0) {
1808             return forcedHeight;
1809         }
1810         final double normalizedWidth = (width >= 0) ? width : -1;
1811         return root.boundedSize(root.prefHeight(normalizedWidth),
1812                                 root.minHeight(normalizedWidth),
1813                                 root.maxHeight(normalizedWidth));
1814     }
1815 
1816     /**
1817      * @treatAsPrivate implementation detail
1818      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
1819      */
1820     @Deprecated
1821     public void impl_preferredSize() {
1822         preferredSize();
1823     }
1824 
1825     private PerformanceTracker tracker;
1826     private static final Object trackerMonitor = new Object();
1827 
1828     // mouse events handling
1829     private MouseHandler mouseHandler;
1830     private ClickGenerator clickGenerator;
1831 
1832     // gesture events handling
1833     private Point2D cursorScreenPos;
1834     private Point2D cursorScenePos;
1835 
1836     private static class TouchGesture {
1837         EventTarget target;
1838         Point2D sceneCoords;
1839         Point2D screenCoords;
1840         boolean finished;
1841     }
1842 
1843     private final TouchGesture scrollGesture = new TouchGesture();
1844     private final TouchGesture zoomGesture = new TouchGesture();
1845     private final TouchGesture rotateGesture = new TouchGesture();
1846     private final TouchGesture swipeGesture = new TouchGesture();
1847 
1848     // touch events handling
1849     private TouchMap touchMap = new TouchMap();
1850     private TouchEvent nextTouchEvent = null;
1851     private TouchPoint[] touchPoints = null;
1852     private int touchEventSetId = 0;
1853     private int touchPointIndex = 0;
1854     private Map<Integer, EventTarget> touchTargets =
1855             new HashMap<Integer, EventTarget>();
1856 
1857     /**
1858      * @treatAsPrivate implementation detail
1859      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
1860      */
1861     // SB-dependency: RT-22747 has been filed to track this
1862     @Deprecated
1863     public void impl_processMouseEvent(MouseEvent e) {
1864         mouseHandler.process(e, false);
1865     }
1866 
1867     private void processMenuEvent(double x2, double y2, double xAbs, double yAbs, boolean isKeyboardTrigger) {
1868         EventTarget eventTarget = null;
1869         Scene.inMousePick = true;
1870         if (isKeyboardTrigger) {
1871             Node sceneFocusOwner = getFocusOwner();
1872 
1873             // for keyboard triggers set coordinates inside focus owner
1874             final double xOffset = xAbs - x2;
1875             final double yOffset = yAbs - y2;
1876             if (sceneFocusOwner != null) {
1877                 final Bounds bounds = sceneFocusOwner.localToScene(
1878                         sceneFocusOwner.getBoundsInLocal());
1879                 x2 = bounds.getMinX() + bounds.getWidth() / 4;
1880                 y2 = bounds.getMinY() + bounds.getHeight() / 2;
1881                 eventTarget = sceneFocusOwner;
1882             } else {
1883                 x2 = Scene.this.getWidth() / 4;


2109 
2110     /**
2111      * Moves the focus to a reasonable initial location. Called when a scene's
2112      * focus is dirty and there's no current owner, or if the owner has been
2113      * removed from the scene.
2114      */
2115     private void focusInitial() {
2116         traversalEngine.traverseToFirst();
2117     }
2118 
2119     /**
2120      * Moves the focus to a reasonble location "near" the given node.
2121      * Called when the focused node is no longer eligible to have
2122      * the focus because it has become invisible or disabled. This
2123      * function assumes that it is still a member of the same scene.
2124      */
2125     private void focusIneligible(Node node) {
2126         traverse(node, Direction.NEXT);
2127     }
2128 
2129     /**
2130      * @treatAsPrivate implementation detail
2131      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2132      */
2133     // SB-dependency: RT-24668 has been filed to track this
2134     @Deprecated
2135     public void impl_processKeyEvent(KeyEvent e) {
2136         if (dndGesture != null) {
2137             if (!dndGesture.processKey(e)) {
2138                 dndGesture = null;
2139             }
2140         }
2141 
2142         getKeyHandler().process(e);
2143     }
2144 
2145     void requestFocus(Node node) {
2146         getKeyHandler().requestFocus(node);
2147     }
2148 
2149     private Node oldFocusOwner;
2150 
2151     /**
2152       * The scene's current focus owner node. This node's "focused"
2153       * variable might be false if this scene has no window, or if the
2154       * window is inactive (window.focused == false).
2155       * @since JavaFX 2.2
2156       */
2157     private ReadOnlyObjectWrapper<Node> focusOwner = new ReadOnlyObjectWrapper<Node>(this, "focusOwner") {
2158 
2159         @Override
2160         protected void invalidated() {
2161             if (oldFocusOwner != null) {
2162                 ((Node.FocusedProperty) oldFocusOwner.focusedProperty()).store(false);
2163             }
2164             Node value = get();
2165             if (value != null) {
2166                 ((Node.FocusedProperty) value.focusedProperty()).store(keyHandler.windowFocused);
2167                 if (value != oldFocusOwner) {
2168                     value.getScene().impl_enableInputMethodEvents(
2169                             value.getInputMethodRequests() != null
2170                             && value.getOnInputMethodTextChanged() != null);
2171                 }
2172             }
2173             // for the rest of the method we need to update the oldFocusOwner
2174             // and use a local copy of it because the user handlers can cause
2175             // recurrent calls of requestFocus
2176             Node localOldOwner = oldFocusOwner;
2177             oldFocusOwner = value;
2178             if (localOldOwner != null) {
2179                 ((Node.FocusedProperty) localOldOwner.focusedProperty()).notifyListeners();
2180             }
2181             if (value != null) {
2182                 ((Node.FocusedProperty) value.focusedProperty()).notifyListeners();
2183             }
2184             PlatformLogger logger = Logging.getFocusLogger();
2185             if (logger.isLoggable(Level.FINE)) {
2186                 if (value == get()) {
2187                     logger.fine("Changed focus from "
2188                             + localOldOwner + " to " + value);


2201     public final Node getFocusOwner() {
2202         return focusOwner.get();
2203     }
2204 
2205     public final ReadOnlyObjectProperty<Node> focusOwnerProperty() {
2206         return focusOwner.getReadOnlyProperty();
2207     }
2208 
2209     // For testing.
2210     void focusCleanup() {
2211         scenePulseListener.focusCleanup();
2212     }
2213 
2214     private void processInputMethodEvent(InputMethodEvent e) {
2215         Node node = getFocusOwner();
2216         if (node != null) {
2217             node.fireEvent(e);
2218         }
2219     }
2220 
2221     /**
2222      * @treatAsPrivate implementation detail
2223      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2224      */
2225     @Deprecated
2226     public void impl_enableInputMethodEvents(boolean enable) {
2227        if (peer != null) {
2228            peer.enableInputMethodEvents(enable);
2229        }
2230     }
2231 
2232     /**
2233      * Returns true if this scene is quiescent, i.e. it has no activity
2234      * pending on it such as CSS processing or layout requests.
2235      *
2236      * Intended to be used for tests only
2237      *
2238      * @return boolean indicating whether the scene is quiescent
2239      */
2240     boolean isQuiescent() {
2241         final Parent r = getRoot();
2242         return !isFocusDirty()
2243                && (r == null || (r.cssFlag == CssFlags.CLEAN &&
2244                 r.layoutFlag == LayoutFlags.CLEAN));
2245     }
2246 


2592                 Scene.this.setY(y);
2593             }
2594         }
2595 
2596         @Override
2597         public void changedSize(float w, float h) {
2598             if (w != Scene.this.getWidth()) Scene.this.setWidth(w);
2599             if (h != Scene.this.getHeight()) Scene.this.setHeight(h);
2600         }
2601 
2602         @Override
2603         public void mouseEvent(EventType<MouseEvent> type, double x, double y, double screenX, double screenY,
2604                                MouseButton button, boolean popupTrigger, boolean synthesized,
2605                                boolean shiftDown, boolean controlDown, boolean altDown, boolean metaDown,
2606                                boolean primaryDown, boolean middleDown, boolean secondaryDown)
2607         {
2608             MouseEvent mouseEvent = new MouseEvent(type, x, y, screenX, screenY, button,
2609                     0, // click count will be adjusted by clickGenerator later anyway
2610                     shiftDown, controlDown, altDown, metaDown,
2611                     primaryDown, middleDown, secondaryDown, synthesized, popupTrigger, false, null);
2612             impl_processMouseEvent(mouseEvent);
2613         }
2614 
2615 
2616         @Override
2617         public void keyEvent(KeyEvent keyEvent)
2618         {
2619             impl_processKeyEvent(keyEvent);
2620         }
2621 
2622         @Override
2623         public void inputMethodEvent(EventType<InputMethodEvent> type,
2624                                      ObservableList<InputMethodTextRun> composed, String committed,
2625                                      int caretPosition)
2626         {
2627             InputMethodEvent inputMethodEvent = new InputMethodEvent(
2628                type, composed, committed, caretPosition);
2629             processInputMethodEvent(inputMethodEvent);
2630         }
2631 
2632         public void menuEvent(double x, double y, double xAbs, double yAbs,
2633                 boolean isKeyboardTrigger) {
2634             Scene.this.processMenuEvent(x, y, xAbs,yAbs, isKeyboardTrigger);
2635         }
2636 
2637         @Override
2638         public void scrollEvent(
2639                 EventType<ScrollEvent> eventType,


4016             PickResultChooser r = new PickResultChooser();
4017             Scene.this.getRoot().impl_pickNode(pickRay, r);
4018             return r.toPickResult();
4019         }
4020     }
4021 
4022     /*******************************************************************************
4023      *                                                                             *
4024      * Key Event Handling                                                          *
4025      *                                                                             *
4026      ******************************************************************************/
4027 
4028     class KeyHandler {
4029         private void setFocusOwner(final Node value) {
4030             // Cancel IM composition if there is one in progress.
4031             // This needs to be done before the focus owner is switched as it
4032             // generates event that needs to be delivered to the old focus owner.
4033             if (oldFocusOwner != null) {
4034                 final Scene s = oldFocusOwner.getScene();
4035                 if (s != null) {
4036                     final TKScene peer = s.impl_getPeer();
4037                     if (peer != null) {
4038                         peer.finishInputMethodComposition();
4039                     }
4040                 }
4041             }
4042             focusOwner.set(value);
4043         }
4044 
4045         private boolean windowFocused;
4046         protected boolean isWindowFocused() { return windowFocused; }
4047         protected void setWindowFocused(boolean value) {
4048             windowFocused = value;
4049             if (getFocusOwner() != null) {
4050                 getFocusOwner().setFocused(windowFocused);
4051             }
4052             if (windowFocused) {
4053                 if (accessible != null) {
4054                     accessible.sendNotification(AccessibleAttribute.FOCUS_NODE);
4055                 }
4056             }


6426             }
6427             accMap.clear();
6428         }
6429     }
6430 
6431     private Accessible accessible;
6432     Accessible getAccessible() {
6433         /*
6434          * The accessible for the Scene should never be
6435          * requested when the peer is not set.
6436          * This can only happen in a error case where a
6437          * descender of this Scene was not disposed and
6438          * it still being used by the AT client and trying
6439          * to reach to the top level window.
6440          */
6441         if (peer == null) return null;
6442         if (accessible == null) {
6443             accessible = Application.GetApplication().createAccessible();
6444             accessible.setEventHandler(new Accessible.EventHandler() {
6445                 @Override public AccessControlContext getAccessControlContext() {
6446                     return impl_getPeer().getAccessControlContext();
6447                 }
6448 
6449                 @Override public Object getAttribute(AccessibleAttribute attribute,
6450                                                      Object... parameters) {
6451                     switch (attribute) {
6452                         case CHILDREN: {
6453                             Parent root = getRoot();
6454                             if (root != null) {
6455                                 return FXCollections.observableArrayList(root);
6456                             }
6457                             break;
6458                         }
6459                         case TEXT: {
6460                             Window w = getWindow();
6461                             if (w instanceof Stage) {
6462                                 return ((Stage)w).getTitle();
6463                             }
6464                             break;
6465                         }
6466                         case NODE_AT_POINT: {




 351         init(width, height);
 352         setFill(fill);
 353     }
 354 
 355     static {
 356             PerformanceTracker.setSceneAccessor(new PerformanceTracker.SceneAccessor() {
 357                 public void setPerfTracker(Scene scene, PerformanceTracker tracker) {
 358                     synchronized (trackerMonitor) {
 359                         scene.tracker = tracker;
 360                     }
 361                 }
 362                 public PerformanceTracker getPerfTracker(Scene scene) {
 363                     synchronized (trackerMonitor) {
 364                         return scene.tracker;
 365                     }
 366                 }
 367             });
 368             SceneHelper.setSceneAccessor(
 369                     new SceneHelper.SceneAccessor() {
 370                         @Override
 371                         public void enableInputMethodEvents(Scene scene, boolean enable) {
 372                             scene.enableInputMethodEvents(enable);
 373                         }
 374 
 375                         @Override
 376                         public void processKeyEvent(Scene scene, KeyEvent e) {
 377                             scene.processKeyEvent(e);
 378                         }
 379 
 380                         @Override
 381                         public void processMouseEvent(Scene scene, MouseEvent e) {
 382                             scene.processMouseEvent(e);
 383                         }
 384 
 385                         @Override
 386                         public void preferredSize(Scene scene) {
 387                             scene.preferredSize();
 388                         }
 389 
 390                         @Override
 391                         public void disposePeer(Scene scene) {
 392                             scene.disposePeer();
 393                         }
 394 
 395                         @Override
 396                         public void initPeer(Scene scene) {
 397                             scene.initPeer();
 398                         }
 399 
 400                         @Override
 401                         public void setWindow(Scene scene, Window window) {
 402                             scene.setWindow(window);
 403                         }
 404 
 405                         @Override
 406                         public TKPulseListener getScenePulseListener(Scene scene) {
 407                             return scene.getScenePulseListener();
 408                         }
 409 
 410                         @Override
 411                         public TKScene getPeer(Scene scene) {
 412                             return scene.getPeer();
 413                         }
 414 
 415                         @Override
 416                         public void setAllowPGAccess(boolean flag) {
 417                             Scene.setAllowPGAccess(flag);
 418                         }
 419 
 420                         @Override
 421                         public void setPaused(boolean paused) {
 422                             Scene.paused = paused;
 423                         }
 424 
 425                         @Override
 426                         public void parentEffectiveOrientationInvalidated(
 427                                 final Scene scene) {
 428                             scene.parentEffectiveOrientationInvalidated();
 429                         }
 430 
 431                         @Override
 432                         public Camera getEffectiveCamera(Scene scene) {
 433                             return scene.getEffectiveCamera();
 434                         }
 435 
 436                         @Override
 437                         public Scene createPopupScene(Parent root) {
 438                             return new Scene(root) {
 439                                        @Override
 440                                        void doLayoutPass() {


 470 
 471         // For debugging
 472         private static boolean inSynchronizer = false;
 473         private static boolean inMousePick = false;
 474         private static boolean allowPGAccess = false;
 475         private static int pgAccessCount = 0;
 476 
 477         // Flag set by the Toolkit when we are paused for JMX debugging
 478         private static boolean paused = false;
 479 
 480         /**
 481          * Used for debugging purposes. Returns true if we are in either the
 482          * mouse event code (picking) or the synchronizer, or if the scene is
 483          * not yet initialized,
 484          *
 485          */
 486         static boolean isPGAccessAllowed() {
 487             return inSynchronizer || inMousePick || allowPGAccess;
 488         }
 489 
 490         static void setAllowPGAccess(boolean flag) {





 491             if (Utils.assertionEnabled()) {
 492                 if (flag) {
 493                     pgAccessCount++;
 494                     allowPGAccess = true;
 495                 }
 496                 else {
 497                     if (pgAccessCount <= 0) {
 498                         throw new java.lang.AssertionError("*** pgAccessCount underflow");
 499                     }
 500                     if (--pgAccessCount == 0) {
 501                         allowPGAccess = false;
 502                     }
 503                 }
 504             }
 505         }
 506 
 507         /**
 508          * If true, use the platform's drag gesture detection
 509          * else use Scene-level detection as per DnDGesture.process(MouseEvent, List)
 510          */


 572             // The dirty bit isn't checked but we must ensure it is cleared.
 573             // The cssFlag is set to clean in either Node.processCSS or
 574             // Node.impl_processCSS(boolean)
 575             sceneRoot.impl_clearDirty(com.sun.javafx.scene.DirtyBits.NODE_CSS);
 576             sceneRoot.processCSS();
 577         }
 578     }
 579 
 580     void doLayoutPass() {
 581         final Parent r = getRoot();
 582         if (r != null) {
 583             r.layout();
 584         }
 585     }
 586 
 587     /**
 588      * The peer of this scene
 589      */
 590     private TKScene peer;
 591 
 592     /*
 593      * Get Scene's peer



 594      */
 595     TKScene getPeer() {

 596         return peer;
 597     }
 598 
 599     /**
 600      * The scene pulse listener that gets called on toolkit pulses
 601      */
 602     ScenePulseListener scenePulseListener = new ScenePulseListener();
 603 
 604     TKPulseListener getScenePulseListener() {





 605         if (SystemProperties.isDebug()) {
 606             return scenePulseListener;
 607         }
 608         return null;
 609     }
 610 
 611     private List<Runnable> preLayoutPulseListeners;
 612     private List<Runnable> postLayoutPulseListeners;
 613 
 614     /**
 615      * Adds a new scene pre layout pulse listener to this scene. Every time a pulse occurs,
 616      * this listener will be called on the JavaFX Application Thread directly
 617      * <strong>before</strong> the CSS and layout passes, and also before
 618      * any rendering is done for
 619      * this frame. This scene pulse listener is suitable for knowing when a
 620      * scenegraph pulse is happening and also for modifying the scenegraph
 621      * (as it is called before CSS and layout, so any changes made will be properly
 622      * styled and positioned).
 623      *
 624      * This method must be called on the JavaFX Application thread.


 736      * and {@link javafx.scene.SceneAntialiasing SceneAntialiasing}
 737      * for more information.
 738      * @since JavaFX 8.0
 739      */
 740     public final SceneAntialiasing getAntiAliasing() {
 741         return antiAliasing;
 742     }
 743 
 744     private boolean getAntiAliasingInternal() {
 745         return (antiAliasing != null &&
 746                 Toolkit.getToolkit().isMSAASupported() &&
 747                 Platform.isSupported(ConditionalFeature.SCENE3D)) ?
 748                 antiAliasing != SceneAntialiasing.DISABLED : false;
 749     }
 750 
 751     /**
 752      * The {@code Window} for this {@code Scene}
 753      */
 754     private ReadOnlyObjectWrapper<Window> window;
 755 
 756     void setWindow(Window value) {
 757         windowPropertyImpl().set(value);
 758     }
 759 
 760     public final Window getWindow() {
 761         return window == null ? null : window.get();
 762     }
 763 
 764     public final ReadOnlyObjectProperty<Window> windowProperty() {
 765         return windowPropertyImpl().getReadOnlyProperty();
 766     }
 767 
 768     private ReadOnlyObjectWrapper<Window> windowPropertyImpl() {
 769         if (window == null) {
 770             window = new ReadOnlyObjectWrapper<Window>() {
 771                 private Window oldWindow;
 772 
 773                 @Override protected void invalidated() {
 774                     final Window newWindow = get();
 775                     getKeyHandler().windowForSceneChanged(oldWindow, newWindow);
 776                     if (oldWindow != null) {
 777                         disposePeer();
 778                     }
 779                     if (newWindow != null) {
 780                         initPeer();
 781                     }
 782                     parentEffectiveOrientationInvalidated();
 783 
 784                     oldWindow = newWindow;
 785                 }
 786 
 787                 @Override
 788                 public Object getBean() {
 789                     return Scene.this;
 790                 }
 791 
 792                 @Override
 793                 public String getName() {
 794                     return "window";
 795                 }
 796             };
 797         }
 798         return window;
 799     }
 800 
 801     void initPeer() {














 802         assert peer == null;
 803 
 804         Window window = getWindow();
 805         // impl_initPeer() is only called from Window, either when the window
 806         // is being shown, or the window scene is being changed. In any case
 807         // this scene's window cannot be null.
 808         assert window != null;
 809 
 810         TKStage windowPeer = WindowHelper.getPeer(window);
 811         if (windowPeer == null) {
 812             // This is fine, the window is not visible. impl_initPeer() will
 813             // be called again later, when the window is being shown.
 814             return;
 815         }
 816 
 817         final boolean isTransparentWindowsSupported = Platform.isSupported(ConditionalFeature.TRANSPARENT_WINDOW);
 818         if (!isTransparentWindowsSupported) {
 819             PlatformImpl.addNoTransparencyStylesheetToScene(this);
 820         }
 821 
 822         PerformanceTracker.logEvent("Scene.initPeer started");
 823 
 824         setAllowPGAccess(true);
 825 
 826         Toolkit tk = Toolkit.getToolkit();
 827         peer = windowPeer.createTKScene(isDepthBufferInternal(), getAntiAliasingInternal(), acc);
 828         PerformanceTracker.logEvent("Scene.initPeer TKScene created");
 829         peer.setTKSceneListener(new ScenePeerListener());
 830         peer.setTKScenePaintListener(new ScenePeerPaintListener());
 831         PerformanceTracker.logEvent("Scene.initPeer TKScene set");
 832         peer.setRoot(getRoot().impl_getPeer());
 833         peer.setFillPaint(getFill() == null ? null : tk.getPaint(getFill()));
 834         getEffectiveCamera().impl_updatePeer();
 835         peer.setCamera((NGCamera) getEffectiveCamera().impl_getPeer());
 836         peer.markDirty();
 837         PerformanceTracker.logEvent("Scene.initPeer TKScene initialized");
 838 
 839         setAllowPGAccess(false);
 840 
 841         tk.addSceneTkPulseListener(scenePulseListener);
 842         // listen to dnd gestures coming from the platform
 843         if (PLATFORM_DRAG_GESTURE_INITIATION) {
 844             if (dragGestureListener == null) {
 845                 dragGestureListener = new DragGestureListener();
 846             }
 847             tk.registerDragGestureListener(peer, EnumSet.allOf(TransferMode.class), dragGestureListener);
 848         }
 849         tk.enableDrop(peer, new DropTargetListener());
 850         tk.installInputMethodRequests(peer, new InputMethodRequestsDelegate());
 851 
 852         PerformanceTracker.logEvent("Scene.initPeer finished");
 853     }
 854 
 855     public void disposePeer() {





 856         if (peer == null) {
 857             // This is fine, the window is either not shown yet and there is no
 858             // need in disposing scene peer, or is hidden and impl_disposePeer()
 859             // has already been called.
 860             return;
 861         }
 862 
 863         PerformanceTracker.logEvent("Scene.disposePeer started");
 864 
 865         Toolkit tk = Toolkit.getToolkit();
 866         tk.removeSceneTkPulseListener(scenePulseListener);
 867         if (accessible != null) {
 868             disposeAccessibles();
 869             Node root = getRoot();
 870             if (root != null) root.releaseAccessible();
 871             accessible.dispose();
 872             accessible = null;
 873         }
 874         peer.dispose();
 875         peer = null;


1299             double x, double y, double w, double h,
1300             Node root, BaseTransform transform, boolean depthBuffer,
1301             Paint fill, Camera camera, WritableImage wimg) {
1302 
1303         Toolkit tk = Toolkit.getToolkit();
1304         Toolkit.ImageRenderingContext context = new Toolkit.ImageRenderingContext();
1305 
1306         int xMin = (int)Math.floor(x);
1307         int yMin = (int)Math.floor(y);
1308         int xMax = (int)Math.ceil(x + w);
1309         int yMax = (int)Math.ceil(y + h);
1310         int width = Math.max(xMax - xMin, 1);
1311         int height = Math.max(yMax - yMin, 1);
1312         if (wimg == null) {
1313             wimg = new WritableImage(width, height);
1314         } else {
1315             width = (int)wimg.getWidth();
1316             height = (int)wimg.getHeight();
1317         }
1318 
1319         setAllowPGAccess(true);
1320         context.x = xMin;
1321         context.y = yMin;
1322         context.width = width;
1323         context.height = height;
1324         context.transform = transform;
1325         context.depthBuffer = depthBuffer;
1326         context.root = root.impl_getPeer();
1327         context.platformPaint = fill == null ? null : tk.getPaint(fill);
1328         double cameraViewWidth = 1.0;
1329         double cameraViewHeight = 1.0;
1330         if (camera != null) {
1331             // temporarily adjust camera viewport to the snapshot size
1332             cameraViewWidth = camera.getViewWidth();
1333             cameraViewHeight = camera.getViewHeight();
1334             camera.setViewWidth(width);
1335             camera.setViewHeight(height);
1336             camera.impl_updatePeer();
1337             context.camera = camera.impl_getPeer();
1338         } else {
1339             context.camera = null;
1340         }
1341 
1342         // Grab the lights from the scene
1343         context.lights = null;
1344         if (scene != null && !scene.lights.isEmpty()) {
1345             context.lights = new NGLightBase[scene.lights.size()];
1346             for (int i = 0; i < scene.lights.size(); i++) {
1347                 context.lights[i] = scene.lights.get(i).impl_getPeer();
1348             }
1349         }
1350 
1351         Toolkit.WritableImageAccessor accessor = Toolkit.getWritableImageAccessor();
1352         context.platformImage = accessor.getTkImageLoader(wimg);
1353         setAllowPGAccess(false);
1354         Object tkImage = tk.renderToImage(context);
1355         accessor.loadTkImage(wimg, tkImage);
1356 
1357         if (camera != null) {
1358             setAllowPGAccess(true);
1359             camera.setViewWidth(cameraViewWidth);
1360             camera.setViewHeight(cameraViewHeight);
1361             camera.impl_updatePeer();
1362             setAllowPGAccess(false);
1363         }
1364 
1365         // if this scene belongs to some stage
1366         // we need to mark the entire scene as dirty
1367         // because dirty logic is buggy
1368         if (scene != null && scene.peer != null) {
1369             scene.setNeedsRepaint();
1370         }
1371 
1372         return wimg;
1373     }
1374 
1375     /**
1376      * Implementation method for snapshot
1377      */
1378     private WritableImage doSnapshot(WritableImage img) {
1379         // TODO: no need to do CSS, layout or sync in the deferred case,
1380         // if this scene is attached to a visible stage
1381         doCSSLayoutSyncForSnapshot(getRoot());
1382 


1736         }
1737         if (height >= 0) {
1738             heightSetByUser = height;
1739             setHeight((float)height);
1740         }
1741         sizeInitialized = (widthSetByUser >= 0 && heightSetByUser >= 0);
1742     }
1743 
1744     private void init() {
1745         if (PerformanceTracker.isLoggingEnabled()) {
1746             PerformanceTracker.logEvent("Scene.init for [" + this + "]");
1747         }
1748         mouseHandler = new MouseHandler();
1749         clickGenerator = new ClickGenerator();
1750 
1751         if (PerformanceTracker.isLoggingEnabled()) {
1752             PerformanceTracker.logEvent("Scene.init for [" + this + "] - finished");
1753         }
1754     }
1755 
1756     void preferredSize() {
1757         final Parent root = getRoot();
1758 
1759         // one or the other isn't initialized, need to perform layout in
1760         // order to ensure we can properly measure the preferred size of the
1761         // scene
1762         doCSSPass();
1763 
1764         resizeRootToPreferredSize(root);
1765         doLayoutPass();
1766 
1767         if (widthSetByUser < 0) {
1768             setWidth(root.isResizable()? root.getLayoutX() + root.getTranslateX() + root.getLayoutBounds().getWidth() :
1769                             root.getBoundsInParent().getMaxX());
1770         } else {
1771             setWidth(widthSetByUser);
1772         }
1773 
1774         if (heightSetByUser < 0) {
1775             setHeight(root.isResizable()? root.getLayoutY() + root.getTranslateY() + root.getLayoutBounds().getHeight() :
1776                             root.getBoundsInParent().getMaxY());


1813             return forcedWidth;
1814         }
1815         final double normalizedHeight = (height >= 0) ? height : -1;
1816         return root.boundedSize(root.prefWidth(normalizedHeight),
1817                                 root.minWidth(normalizedHeight),
1818                                 root.maxWidth(normalizedHeight));
1819     }
1820 
1821     private static double getPreferredHeight(Parent root,
1822                                              double forcedHeight,
1823                                              double width) {
1824         if (forcedHeight >= 0) {
1825             return forcedHeight;
1826         }
1827         final double normalizedWidth = (width >= 0) ? width : -1;
1828         return root.boundedSize(root.prefHeight(normalizedWidth),
1829                                 root.minHeight(normalizedWidth),
1830                                 root.maxHeight(normalizedWidth));
1831     }
1832 









1833     private PerformanceTracker tracker;
1834     private static final Object trackerMonitor = new Object();
1835 
1836     // mouse events handling
1837     private MouseHandler mouseHandler;
1838     private ClickGenerator clickGenerator;
1839 
1840     // gesture events handling
1841     private Point2D cursorScreenPos;
1842     private Point2D cursorScenePos;
1843 
1844     private static class TouchGesture {
1845         EventTarget target;
1846         Point2D sceneCoords;
1847         Point2D screenCoords;
1848         boolean finished;
1849     }
1850 
1851     private final TouchGesture scrollGesture = new TouchGesture();
1852     private final TouchGesture zoomGesture = new TouchGesture();
1853     private final TouchGesture rotateGesture = new TouchGesture();
1854     private final TouchGesture swipeGesture = new TouchGesture();
1855 
1856     // touch events handling
1857     private TouchMap touchMap = new TouchMap();
1858     private TouchEvent nextTouchEvent = null;
1859     private TouchPoint[] touchPoints = null;
1860     private int touchEventSetId = 0;
1861     private int touchPointIndex = 0;
1862     private Map<Integer, EventTarget> touchTargets =
1863             new HashMap<Integer, EventTarget>();
1864 
1865     void processMouseEvent(MouseEvent e) {






1866         mouseHandler.process(e, false);
1867     }
1868 
1869     private void processMenuEvent(double x2, double y2, double xAbs, double yAbs, boolean isKeyboardTrigger) {
1870         EventTarget eventTarget = null;
1871         Scene.inMousePick = true;
1872         if (isKeyboardTrigger) {
1873             Node sceneFocusOwner = getFocusOwner();
1874 
1875             // for keyboard triggers set coordinates inside focus owner
1876             final double xOffset = xAbs - x2;
1877             final double yOffset = yAbs - y2;
1878             if (sceneFocusOwner != null) {
1879                 final Bounds bounds = sceneFocusOwner.localToScene(
1880                         sceneFocusOwner.getBoundsInLocal());
1881                 x2 = bounds.getMinX() + bounds.getWidth() / 4;
1882                 y2 = bounds.getMinY() + bounds.getHeight() / 2;
1883                 eventTarget = sceneFocusOwner;
1884             } else {
1885                 x2 = Scene.this.getWidth() / 4;


2111 
2112     /**
2113      * Moves the focus to a reasonable initial location. Called when a scene's
2114      * focus is dirty and there's no current owner, or if the owner has been
2115      * removed from the scene.
2116      */
2117     private void focusInitial() {
2118         traversalEngine.traverseToFirst();
2119     }
2120 
2121     /**
2122      * Moves the focus to a reasonble location "near" the given node.
2123      * Called when the focused node is no longer eligible to have
2124      * the focus because it has become invisible or disabled. This
2125      * function assumes that it is still a member of the same scene.
2126      */
2127     private void focusIneligible(Node node) {
2128         traverse(node, Direction.NEXT);
2129     }
2130 
2131     public void processKeyEvent(KeyEvent e) {






2132         if (dndGesture != null) {
2133             if (!dndGesture.processKey(e)) {
2134                 dndGesture = null;
2135             }
2136         }
2137 
2138         getKeyHandler().process(e);
2139     }
2140 
2141     void requestFocus(Node node) {
2142         getKeyHandler().requestFocus(node);
2143     }
2144 
2145     private Node oldFocusOwner;
2146 
2147     /**
2148       * The scene's current focus owner node. This node's "focused"
2149       * variable might be false if this scene has no window, or if the
2150       * window is inactive (window.focused == false).
2151       * @since JavaFX 2.2
2152       */
2153     private ReadOnlyObjectWrapper<Node> focusOwner = new ReadOnlyObjectWrapper<Node>(this, "focusOwner") {
2154 
2155         @Override
2156         protected void invalidated() {
2157             if (oldFocusOwner != null) {
2158                 ((Node.FocusedProperty) oldFocusOwner.focusedProperty()).store(false);
2159             }
2160             Node value = get();
2161             if (value != null) {
2162                 ((Node.FocusedProperty) value.focusedProperty()).store(keyHandler.windowFocused);
2163                 if (value != oldFocusOwner) {
2164                     value.getScene().enableInputMethodEvents(
2165                             value.getInputMethodRequests() != null
2166                             && value.getOnInputMethodTextChanged() != null);
2167                 }
2168             }
2169             // for the rest of the method we need to update the oldFocusOwner
2170             // and use a local copy of it because the user handlers can cause
2171             // recurrent calls of requestFocus
2172             Node localOldOwner = oldFocusOwner;
2173             oldFocusOwner = value;
2174             if (localOldOwner != null) {
2175                 ((Node.FocusedProperty) localOldOwner.focusedProperty()).notifyListeners();
2176             }
2177             if (value != null) {
2178                 ((Node.FocusedProperty) value.focusedProperty()).notifyListeners();
2179             }
2180             PlatformLogger logger = Logging.getFocusLogger();
2181             if (logger.isLoggable(Level.FINE)) {
2182                 if (value == get()) {
2183                     logger.fine("Changed focus from "
2184                             + localOldOwner + " to " + value);


2197     public final Node getFocusOwner() {
2198         return focusOwner.get();
2199     }
2200 
2201     public final ReadOnlyObjectProperty<Node> focusOwnerProperty() {
2202         return focusOwner.getReadOnlyProperty();
2203     }
2204 
2205     // For testing.
2206     void focusCleanup() {
2207         scenePulseListener.focusCleanup();
2208     }
2209 
2210     private void processInputMethodEvent(InputMethodEvent e) {
2211         Node node = getFocusOwner();
2212         if (node != null) {
2213             node.fireEvent(e);
2214         }
2215     }
2216 
2217     public void enableInputMethodEvents(boolean enable) {





2218        if (peer != null) {
2219            peer.enableInputMethodEvents(enable);
2220        }
2221     }
2222 
2223     /**
2224      * Returns true if this scene is quiescent, i.e. it has no activity
2225      * pending on it such as CSS processing or layout requests.
2226      *
2227      * Intended to be used for tests only
2228      *
2229      * @return boolean indicating whether the scene is quiescent
2230      */
2231     boolean isQuiescent() {
2232         final Parent r = getRoot();
2233         return !isFocusDirty()
2234                && (r == null || (r.cssFlag == CssFlags.CLEAN &&
2235                 r.layoutFlag == LayoutFlags.CLEAN));
2236     }
2237 


2583                 Scene.this.setY(y);
2584             }
2585         }
2586 
2587         @Override
2588         public void changedSize(float w, float h) {
2589             if (w != Scene.this.getWidth()) Scene.this.setWidth(w);
2590             if (h != Scene.this.getHeight()) Scene.this.setHeight(h);
2591         }
2592 
2593         @Override
2594         public void mouseEvent(EventType<MouseEvent> type, double x, double y, double screenX, double screenY,
2595                                MouseButton button, boolean popupTrigger, boolean synthesized,
2596                                boolean shiftDown, boolean controlDown, boolean altDown, boolean metaDown,
2597                                boolean primaryDown, boolean middleDown, boolean secondaryDown)
2598         {
2599             MouseEvent mouseEvent = new MouseEvent(type, x, y, screenX, screenY, button,
2600                     0, // click count will be adjusted by clickGenerator later anyway
2601                     shiftDown, controlDown, altDown, metaDown,
2602                     primaryDown, middleDown, secondaryDown, synthesized, popupTrigger, false, null);
2603             processMouseEvent(mouseEvent);
2604         }
2605 
2606 
2607         @Override
2608         public void keyEvent(KeyEvent keyEvent)
2609         {
2610             processKeyEvent(keyEvent);
2611         }
2612 
2613         @Override
2614         public void inputMethodEvent(EventType<InputMethodEvent> type,
2615                                      ObservableList<InputMethodTextRun> composed, String committed,
2616                                      int caretPosition)
2617         {
2618             InputMethodEvent inputMethodEvent = new InputMethodEvent(
2619                type, composed, committed, caretPosition);
2620             processInputMethodEvent(inputMethodEvent);
2621         }
2622 
2623         public void menuEvent(double x, double y, double xAbs, double yAbs,
2624                 boolean isKeyboardTrigger) {
2625             Scene.this.processMenuEvent(x, y, xAbs,yAbs, isKeyboardTrigger);
2626         }
2627 
2628         @Override
2629         public void scrollEvent(
2630                 EventType<ScrollEvent> eventType,


4007             PickResultChooser r = new PickResultChooser();
4008             Scene.this.getRoot().impl_pickNode(pickRay, r);
4009             return r.toPickResult();
4010         }
4011     }
4012 
4013     /*******************************************************************************
4014      *                                                                             *
4015      * Key Event Handling                                                          *
4016      *                                                                             *
4017      ******************************************************************************/
4018 
4019     class KeyHandler {
4020         private void setFocusOwner(final Node value) {
4021             // Cancel IM composition if there is one in progress.
4022             // This needs to be done before the focus owner is switched as it
4023             // generates event that needs to be delivered to the old focus owner.
4024             if (oldFocusOwner != null) {
4025                 final Scene s = oldFocusOwner.getScene();
4026                 if (s != null) {
4027                     final TKScene peer = s.getPeer();
4028                     if (peer != null) {
4029                         peer.finishInputMethodComposition();
4030                     }
4031                 }
4032             }
4033             focusOwner.set(value);
4034         }
4035 
4036         private boolean windowFocused;
4037         protected boolean isWindowFocused() { return windowFocused; }
4038         protected void setWindowFocused(boolean value) {
4039             windowFocused = value;
4040             if (getFocusOwner() != null) {
4041                 getFocusOwner().setFocused(windowFocused);
4042             }
4043             if (windowFocused) {
4044                 if (accessible != null) {
4045                     accessible.sendNotification(AccessibleAttribute.FOCUS_NODE);
4046                 }
4047             }


6417             }
6418             accMap.clear();
6419         }
6420     }
6421 
6422     private Accessible accessible;
6423     Accessible getAccessible() {
6424         /*
6425          * The accessible for the Scene should never be
6426          * requested when the peer is not set.
6427          * This can only happen in a error case where a
6428          * descender of this Scene was not disposed and
6429          * it still being used by the AT client and trying
6430          * to reach to the top level window.
6431          */
6432         if (peer == null) return null;
6433         if (accessible == null) {
6434             accessible = Application.GetApplication().createAccessible();
6435             accessible.setEventHandler(new Accessible.EventHandler() {
6436                 @Override public AccessControlContext getAccessControlContext() {
6437                     return getPeer().getAccessControlContext();
6438                 }
6439 
6440                 @Override public Object getAttribute(AccessibleAttribute attribute,
6441                                                      Object... parameters) {
6442                     switch (attribute) {
6443                         case CHILDREN: {
6444                             Parent root = getRoot();
6445                             if (root != null) {
6446                                 return FXCollections.observableArrayList(root);
6447                             }
6448                             break;
6449                         }
6450                         case TEXT: {
6451                             Window w = getWindow();
6452                             if (w instanceof Stage) {
6453                                 return ((Stage)w).getTitle();
6454                             }
6455                             break;
6456                         }
6457                         case NODE_AT_POINT: {