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: {
|