/* * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javafx.scene; import com.sun.javafx.geometry.BoundsUtils; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; import javafx.beans.binding.BooleanExpression; import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanPropertyBase; import javafx.beans.property.DoubleProperty; import javafx.beans.property.DoublePropertyBase; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectPropertyBase; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanPropertyBase; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectPropertyBase; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.StringProperty; import javafx.beans.property.StringPropertyBase; import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener.Change; import javafx.collections.ObservableList; import javafx.collections.ObservableMap; import javafx.collections.ObservableSet; import javafx.css.CssMetaData; import javafx.css.ParsedValue; import javafx.css.PseudoClass; import javafx.css.StyleConverter; import javafx.css.Styleable; import javafx.css.StyleableBooleanProperty; import javafx.css.StyleableDoubleProperty; import javafx.css.StyleableObjectProperty; import javafx.css.StyleableProperty; import javafx.event.Event; import javafx.event.EventDispatchChain; import javafx.event.EventDispatcher; import javafx.event.EventHandler; import javafx.event.EventTarget; import javafx.event.EventType; import javafx.geometry.BoundingBox; import javafx.geometry.Bounds; import javafx.geometry.NodeOrientation; import javafx.geometry.Orientation; import javafx.geometry.Point2D; import javafx.geometry.Point3D; import javafx.geometry.Rectangle2D; import javafx.scene.effect.BlendMode; import javafx.scene.effect.Effect; import javafx.scene.image.WritableImage; import javafx.scene.input.ContextMenuEvent; import javafx.scene.input.DragEvent; import javafx.scene.input.Dragboard; import javafx.scene.input.InputEvent; import javafx.scene.input.InputMethodEvent; import javafx.scene.input.InputMethodRequests; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseDragEvent; import javafx.scene.input.MouseEvent; import javafx.scene.input.PickResult; import javafx.scene.input.RotateEvent; import javafx.scene.input.ScrollEvent; import javafx.scene.input.SwipeEvent; import javafx.scene.input.TouchEvent; import javafx.scene.input.TransferMode; import javafx.scene.input.ZoomEvent; import javafx.scene.text.Font; import javafx.scene.transform.Rotate; import javafx.scene.transform.Transform; import javafx.stage.Window; import javafx.util.Callback; import java.security.AccessControlContext; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.sun.glass.ui.Accessible; import com.sun.glass.ui.Application; import com.sun.javafx.util.Logging; import com.sun.javafx.util.TempState; import com.sun.javafx.util.Utils; import com.sun.javafx.beans.IDProperty; import com.sun.javafx.beans.event.AbstractNotifyListener; import com.sun.javafx.binding.ExpressionHelper; import com.sun.javafx.collections.TrackableObservableList; import com.sun.javafx.collections.UnmodifiableListSet; import com.sun.javafx.css.PseudoClassState; import javafx.css.Selector; import javafx.css.Style; import javafx.css.converter.BooleanConverter; import javafx.css.converter.CursorConverter; import javafx.css.converter.EffectConverter; import javafx.css.converter.EnumConverter; import javafx.css.converter.SizeConverter; import com.sun.javafx.effect.EffectDirtyBits; import com.sun.javafx.geom.BaseBounds; import com.sun.javafx.geom.BoxBounds; import com.sun.javafx.geom.PickRay; import com.sun.javafx.geom.RectBounds; import com.sun.javafx.geom.Vec3d; import com.sun.javafx.geom.transform.Affine3D; import com.sun.javafx.geom.transform.BaseTransform; import com.sun.javafx.geom.transform.GeneralTransform3D; import com.sun.javafx.geom.transform.NoninvertibleTransformException; import com.sun.javafx.perf.PerformanceTracker; import com.sun.javafx.scene.BoundsAccessor; import com.sun.javafx.scene.CameraHelper; import com.sun.javafx.scene.CssFlags; import com.sun.javafx.scene.DirtyBits; import com.sun.javafx.scene.EventHandlerProperties; import com.sun.javafx.scene.LayoutFlags; import com.sun.javafx.scene.NodeEventDispatcher; import com.sun.javafx.scene.NodeHelper; import com.sun.javafx.scene.SceneHelper; import com.sun.javafx.scene.SceneUtils; import com.sun.javafx.scene.input.PickResultChooser; import com.sun.javafx.scene.transform.TransformHelper; import com.sun.javafx.scene.transform.TransformUtils; import com.sun.javafx.scene.traversal.Direction; import com.sun.javafx.sg.prism.NGNode; import com.sun.javafx.tk.Toolkit; import com.sun.prism.impl.PrismSettings; import com.sun.scenario.effect.EffectHelper; import javafx.scene.shape.Shape3D; import com.sun.javafx.logging.PlatformLogger; import com.sun.javafx.logging.PlatformLogger.Level; /** * Base class for scene graph nodes. A scene graph is a set of tree data structures * where every item has zero or one parent, and each item is either * a "leaf" with zero sub-items or a "branch" with zero or more sub-items. *

* Each item in the scene graph is called a {@code Node}. Branch nodes are * of type {@link Parent}, whose concrete subclasses are {@link Group}, * {@link javafx.scene.layout.Region}, and {@link javafx.scene.control.Control}, * or subclasses thereof. *

* Leaf nodes are classes such as * {@link javafx.scene.shape.Rectangle}, {@link javafx.scene.text.Text}, * {@link javafx.scene.image.ImageView}, {@link javafx.scene.media.MediaView}, * or other such leaf classes which cannot have children. Only a single node within * each scene graph tree will have no parent, which is referred to as the "root" node. *

* There may be several trees in the scene graph. Some trees may be part of * a {@link Scene}, in which case they are eligible to be displayed. * Other trees might not be part of any {@link Scene}. *

* A node may occur at most once anywhere in the scene graph. Specifically, * a node must appear no more than once in all of the following: * as the root node of a {@link Scene}, * the children ObservableList of a {@link Parent}, * or as the clip of a {@link Node}. *

* The scene graph must not have cycles. A cycle would exist if a node is * an ancestor of itself in the tree, considering the {@link Group} content * ObservableList, {@link Parent} children ObservableList, and {@link Node} clip relationships * mentioned above. *

* If a program adds a child node to a Parent (including Group, Region, etc) * and that node is already a child of a different Parent or the root of a Scene, * the node is automatically (and silently) removed from its former parent. * If a program attempts to modify the scene graph in any other way that violates * the above rules, an exception is thrown, the modification attempt is ignored * and the scene graph is restored to its previous state. *

* It is possible to rearrange the structure of the scene graph, for * example, to move a subtree from one location in the scene graph to * another. In order to do this, one would normally remove the subtree from * its old location before inserting it at the new location. However, the * subtree will be automatically removed as described above if the application * doesn't explicitly remove it. *

* Node objects may be constructed and modified on any thread as long they are * not yet attached to a {@link Scene} in a {@link Window} that is * {@link Window#isShowing showing}. * An application must attach nodes to such a Scene or modify them on the JavaFX * Application Thread. * *

* The JavaFX Application Thread is created as part of the startup process for * the JavaFX runtime. See the {@link javafx.application.Application} class and * the {@link Platform#startup(Runnable)} method for more information. *

* *

* An application should not extend the Node class directly. Doing so may lead to * an UnsupportedOperationException being thrown. *

* *

String ID

*

* Each node in the scene graph can be given a unique {@link #idProperty id}. This id is * much like the "id" attribute of an HTML tag in that it is up to the designer * and developer to ensure that the {@code id} is unique within the scene graph. * A convenience function called {@link #lookup(String)} can be used to find * a node with a unique id within the scene graph, or within a subtree of the * scene graph. The id can also be used identify nodes for applying styles; see * the CSS section below. * *

Coordinate System

*

* The {@code Node} class defines a traditional computer graphics "local" * coordinate system in which the {@code x} axis increases to the right and the * {@code y} axis increases downwards. The concrete node classes for shapes * provide variables for defining the geometry and location of the shape * within this local coordinate space. For example, * {@link javafx.scene.shape.Rectangle} provides {@code x}, {@code y}, * {@code width}, {@code height} variables while * {@link javafx.scene.shape.Circle} provides {@code centerX}, {@code centerY}, * and {@code radius}. *

* At the device pixel level, integer coordinates map onto the corners and * cracks between the pixels and the centers of the pixels appear at the * midpoints between integer pixel locations. Because all coordinate values * are specified with floating point numbers, coordinates can precisely * point to these corners (when the floating point values have exact integer * values) or to any location on the pixel. For example, a coordinate of * {@code (0.5, 0.5)} would point to the center of the upper left pixel on the * {@code Stage}. Similarly, a rectangle at {@code (0, 0)} with dimensions * of {@code 10} by {@code 10} would span from the upper left corner of the * upper left pixel on the {@code Stage} to the lower right corner of the * 10th pixel on the 10th scanline. The pixel center of the last pixel * inside that rectangle would be at the coordinates {@code (9.5, 9.5)}. *

* In practice, most nodes have transformations applied to their coordinate * system as mentioned below. As a result, the information above describing * the alignment of device coordinates to the pixel grid is relative to * the transformed coordinates, not the local coordinates of the nodes. * The {@link javafx.scene.shape.Shape Shape} class describes some additional * important context-specific information about coordinate mapping and how * it can affect rendering. * *

Transformations

*

* Any {@code Node} can have transformations applied to it. These include * translation, rotation, scaling, or shearing. *

* A translation transformation is one which shifts the origin of the * node's coordinate space along either the x or y axis. For example, if you * create a {@link javafx.scene.shape.Rectangle} which is drawn at the origin * (x=0, y=0) and has a width of 100 and a height of 50, and then apply a * {@link javafx.scene.transform.Translate} with a shift of 10 along the x axis * (x=10), then the rectangle will appear drawn at (x=10, y=0) and remain * 100 points wide and 50 tall. Note that the origin was shifted, not the * {@code x} variable of the rectangle. *

* A common node transform is a translation by an integer distance, most often * used to lay out nodes on the stage. Such integer translations maintain the * device pixel mapping so that local coordinates that are integers still * map to the cracks between pixels. *

* A rotation transformation is one which rotates the coordinate space of * the node about a specified "pivot" point, causing the node to appear rotated. * For example, if you create a {@link javafx.scene.shape.Rectangle} which is * drawn at the origin (x=0, y=0) and has a width of 100 and height of 30 and * you apply a {@link javafx.scene.transform.Rotate} with a 90 degree rotation * (angle=90) and a pivot at the origin (pivotX=0, pivotY=0), then * the rectangle will be drawn as if its x and y were zero but its height was * 100 and its width -30. That is, it is as if a pin is being stuck at the top * left corner and the rectangle is rotating 90 degrees clockwise around that * pin. If the pivot point is instead placed in the center of the rectangle * (at point x=50, y=15) then the rectangle will instead appear to rotate about * its center. *

* Note that as with all transformations, the x, y, width, and height variables * of the rectangle (which remain relative to the local coordinate space) have * not changed, but rather the transformation alters the entire coordinate space * of the rectangle. *

* A scaling transformation causes a node to either appear larger or * smaller depending on the scaling factor. Scaling alters the coordinate space * of the node such that each unit of distance along the axis in local * coordinates is multiplied by the scale factor. As with rotation * transformations, scaling transformations are applied about a "pivot" point. * You can think of this as the point in the Node around which you "zoom". For * example, if you create a {@link javafx.scene.shape.Rectangle} with a * {@code strokeWidth} of 5, and a width and height of 50, and you apply a * {@link javafx.scene.transform.Scale} with scale factors (x=2.0, y=2.0) and * a pivot at the origin (pivotX=0, pivotY=0), the entire rectangle * (including the stroke) will double in size, growing to the right and * downwards from the origin. *

* A shearing transformation, sometimes called a skew, effectively * rotates one axis so that the x and y axes are no longer perpendicular. *

* Multiple transformations may be applied to a node by specifying an ordered * chain of transforms. The order in which the transforms are applied is * defined by the ObservableList specified in the {@link #getTransforms transforms} variable. * *

Bounding Rectangles

*

* Since every {@code Node} has transformations, every Node's geometric * bounding rectangle can be described differently depending on whether * transformations are accounted for or not. *

* Each {@code Node} has a read-only {@link #boundsInLocalProperty boundsInLocal} * variable which specifies the bounding rectangle of the {@code Node} in * untransformed local coordinates. {@code boundsInLocal} includes the * Node's shape geometry, including any space required for a * non-zero stroke that may fall outside the local position/size variables, * and its {@link #clipProperty clip} and {@link #effectProperty effect} variables. *

* Each {@code Node} also has a read-only {@link #boundsInParentProperty boundsInParent} variable which * specifies the bounding rectangle of the {@code Node} after all transformations * have been applied, including those set in {@link #getTransforms transforms}, * {@link #scaleXProperty scaleX}/{@link #scaleYProperty scaleY}, {@link #rotateProperty rotate}, * {@link #translateXProperty translateX}/{@link #translateYProperty translateY}, and {@link #layoutXProperty layoutX}/{@link #layoutYProperty layoutY}. * It is called "boundsInParent" because the rectangle will be relative to the * parent's coordinate system. This is the 'visual' bounds of the node. *

* Finally, the {@link #layoutBoundsProperty layoutBounds} variable defines the rectangular bounds of * the {@code Node} that should be used as the basis for layout calculations and * may differ from the visual bounds of the node. For shapes, Text, and ImageView, * layoutBounds by default includes only the shape geometry, including space required * for a non-zero {@code strokeWidth}, but does not include the effect, * clip, or any transforms. For resizable classes (Regions and Controls) * layoutBounds will always map to {@code 0,0 width x height}. * *

The image shows a node without any transformation and its {@code boundsInLocal}: *

A sine wave shape enclosed by
 * an axis-aligned rectangular bounds

* If we rotate the image by 20 degrees we get following result: *

An axis-aligned rectangular
 * bounds that encloses the shape rotated by 20 degrees

* The red rectangle represents {@code boundsInParent} in the * coordinate space of the Node's parent. The {@code boundsInLocal} stays the same * as in the first image, the green rectangle in this image represents {@code boundsInLocal} * in the coordinate space of the Node. * *

The images show a filled and stroked rectangle and their bounds. The * first rectangle {@code [x:10.0 y:10.0 width:100.0 height:100.0 strokeWidth:0]} * has the following bounds bounds: {@code [x:10.0 y:10.0 width:100.0 height:100.0]}. * * The second rectangle {@code [x:10.0 y:10.0 width:100.0 height:100.0 strokeWidth:5]} * has the following bounds: {@code [x:7.5 y:7.5 width:105 height:105]} * (the stroke is centered by default, so only half of it is outside * of the original bounds; it is also possible to create inside or outside * stroke). * * Since neither of the rectangles has any transformation applied, * {@code boundsInParent} and {@code boundsInLocal} are the same.

*

The rectangles are enclosed by their
 * respective bounds

* * *

CSS

*

* The {@code Node} class contains {@code id}, {@code styleClass}, and * {@code style} variables that are used in styling this node from * CSS. The {@code id} and {@code styleClass} variables are used in * CSS style sheets to identify nodes to which styles should be * applied. The {@code style} variable contains style properties and * values that are applied directly to this node. *

* For further information about CSS and how to apply CSS styles * to nodes, see the CSS Reference * Guide. * @since JavaFX 2.0 */ @IDProperty("id") public abstract class Node implements EventTarget, Styleable { /* * Store the singleton instance of the NodeHelper subclass corresponding * to the subclass of this instance of Node */ private NodeHelper nodeHelper = null; static { PerformanceTracker.logEvent("Node class loaded"); // This is used by classes in different packages to get access to // private and package private methods. NodeHelper.setNodeAccessor(new NodeHelper.NodeAccessor() { @Override public NodeHelper getHelper(Node node) { return node.nodeHelper; } @Override public void setHelper(Node node, NodeHelper nodeHelper) { node.nodeHelper = nodeHelper; } @Override public void doMarkDirty(Node node, DirtyBits dirtyBit) { node.doMarkDirty(dirtyBit); } @Override public void doUpdatePeer(Node node) { node.doUpdatePeer(); } @Override public BaseTransform getLeafTransform(Node node) { return node.getLeafTransform(); } @Override public Bounds doComputeLayoutBounds(Node node) { return node.doComputeLayoutBounds(); } @Override public void doTransformsChanged(Node node) { node.doTransformsChanged(); } @Override public void doPickNodeLocal(Node node, PickRay localPickRay, PickResultChooser result) { node.doPickNodeLocal(localPickRay, result); } @Override public boolean doComputeIntersects(Node node, PickRay pickRay, PickResultChooser pickResult) { return node.doComputeIntersects(pickRay, pickResult); } @Override public void doGeomChanged(Node node) { node.doGeomChanged(); } @Override public void doNotifyLayoutBoundsChanged(Node node) { node.doNotifyLayoutBoundsChanged(); } @Override public void doProcessCSS(Node node) { node.doProcessCSS(); } @Override public boolean isDirty(Node node, DirtyBits dirtyBit) { return node.isDirty(dirtyBit); } @Override public boolean isDirtyEmpty(Node node) { return node.isDirtyEmpty(); } @Override public void syncPeer(Node node) { node.syncPeer(); } @Override public void layoutBoundsChanged(Node node) { node.layoutBoundsChanged(); } @Override public

P getPeer(Node node) { return node.getPeer(); } @Override public void setShowMnemonics(Node node, boolean value) { node.setShowMnemonics(value); } @Override public boolean isShowMnemonics(Node node) { return node.isShowMnemonics(); } @Override public BooleanProperty showMnemonicsProperty(Node node) { return node.showMnemonicsProperty(); } @Override public boolean traverse(Node node, Direction direction) { return node.traverse(direction); } @Override public double getPivotX(Node node) { return node.getPivotX(); } @Override public double getPivotY(Node node) { return node.getPivotY(); } @Override public double getPivotZ(Node node) { return node.getPivotZ(); } @Override public void pickNode(Node node,PickRay pickRay, PickResultChooser result) { node.pickNode(pickRay, result); } @Override public boolean intersects(Node node, PickRay pickRay, PickResultChooser pickResult) { return node.intersects(pickRay, pickResult); } @Override public double intersectsBounds(Node node, PickRay pickRay) { return node.intersectsBounds(pickRay); } @Override public void layoutNodeForPrinting(Node node) { node.doCSSLayoutSyncForSnapshot(); } @Override public boolean isDerivedDepthTest(Node node) { return node.isDerivedDepthTest(); } @Override public SubScene getSubScene(Node node) { return node.getSubScene(); } @Override public void setLabeledBy(Node node, Node labeledBy) { node.labeledBy = labeledBy; } @Override public Accessible getAccessible(Node node) { return node.getAccessible(); } @Override public void reapplyCSS(Node node) { node.reapplyCSS(); } @Override public boolean isTreeVisible(Node node) { return node.isTreeVisible(); } @Override public BooleanExpression treeVisibleProperty(Node node) { return node.treeVisibleProperty(); } @Override public boolean isTreeShowing(Node node) { return node.isTreeShowing(); } @Override public BooleanExpression treeShowingProperty(Node node) { return node.treeShowingProperty(); } @Override public List