modules/graphics/src/main/java/javafx/scene/Parent.java
Print this page
*** 59,68 ****
--- 59,70 ----
import com.sun.javafx.scene.input.PickResultChooser;
import com.sun.javafx.sg.prism.NGGroup;
import com.sun.javafx.sg.prism.NGNode;
import com.sun.javafx.tk.Toolkit;
import com.sun.javafx.scene.LayoutFlags;
+ import com.sun.javafx.scene.ParentHelper;
+ import java.util.Collections;
import javafx.stage.Window;
/**
* The base class for all nodes that have children in the scene graph.
* <p>
*** 95,104 ****
--- 97,118 ----
* Do not populate list of removed children when its number exceeds threshold,
* but mark whole parent dirty.
*/
private boolean removedChildrenOptimizationDisabled = false;
+ static {
+ // This is used by classes in different packages to get access to
+ // private and package private methods.
+ ParentHelper.setParentAccessor(new ParentHelper.ParentAccessor() {
+
+ @Override
+ public boolean pickChildrenNode(Parent parent, PickRay pickRay, PickResultChooser result) {
+ return parent.pickChildrenNode(pickRay, result);
+ }
+ });
+ }
+
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended for use and will be removed in the next version
*/
@Deprecated
*** 138,147 ****
--- 152,165 ----
}
pgChildrenSize = children.size();
startIdx = pgChildrenSize;
}
+ if (sortedChildrenDirty) {
+ computeSortedChidrenAndUpdatePeer();
+ sortedChildrenDirty = false;
+ }
if (Utils.assertionEnabled()) validatePG();
}
/***********************************************************************
*** 193,202 ****
--- 211,279 ----
str += nn + " ";
}
System.out.println(str);
}
+ /**
+ * The sortedChildren list is sorted by the children's priorityOrder if it is not null.
+ * Its size should always be equal to children.size().
+ * If sortedChildren is null it implies that the rendering order of the children
+ * is the same as the order in the children list.
+ */
+ final private List<Node> sortedChildren = new ArrayList(1);
+ private boolean sortedChildrenDirty = false;
+
+ void markSortedChildrenDirty() {
+ if (!sortedChildrenDirty) {
+ sortedChildrenDirty = true;
+ impl_markDirty(DirtyBits.NODE_PRIORITY_ORDER);
+ }
+ }
+
+ private void computeSortedChidrenAndUpdatePeer() {
+ boolean priorityOrderSet = false;
+ for (Node child : children) {
+ // Need to update children of peer before sorting
+ NGNode childPeer = child.impl_getPeer();
+ double po = child.getPriorityOrder();
+ childPeer.setPriorityOrder(po);
+
+ if (!priorityOrderSet && po != 0) {
+ priorityOrderSet = true;
+ }
+ }
+
+ final NGGroup peer = impl_getPeer();
+ List<NGNode> peerSortedChildren = peer.getSortedChildren();
+ sortedChildren.clear();
+ peerSortedChildren.clear();
+
+ if (priorityOrderSet) {
+ sortedChildren.addAll(children);
+
+ // Sort in descending order (or big-to-small order)
+ Collections.sort(sortedChildren, (Node a, Node b)
+ -> a.getPriorityOrder() < b.getPriorityOrder() ? 1
+ : a.getPriorityOrder() == b.getPriorityOrder() ? 0 : -1);
+
+ for (Node child : sortedChildren) {
+ NGNode childPeer = child.impl_getPeer();
+ peerSortedChildren.add(childPeer);
+ }
+ }
+
+ }
+
+ // Call this method if children order is needed for rendering and picking.
+ // The returned list should be treated as read only.
+ private List<Node> getOrderedChildren() {
+ if (!sortedChildren.isEmpty()) {
+ return sortedChildren;
+ }
+ return children;
+ }
+
// Variable used to indicate that the change to the children ObservableList is
// a simple permutation as the result of a toFront or toBack operation.
// We can avoid almost all of the processing of the on replace trigger in
// this case.
private boolean childrenTriggerPermutation = false;
*** 257,269 ****
--- 334,356 ----
if (n.isManaged()) {
relayout = true;
}
}
+ // Mark sortedChildrenDirty if there is modification to children list
+ // because priorty order was set on one of more children
+ if (((removedSize > 0) || (to - from) > 0) && !sortedChildren.isEmpty()) {
+ sortedChildrenDirty = true;
+ }
// update the parent and scene for each new node
for (int i = from; i < to; ++i) {
Node node = children.get(i);
+
+ // Newly added node has priority order set.
+ if (node.getPriorityOrder() != 0) {
+ sortedChildrenDirty = true;
+ }
if (node.isManaged() || (node instanceof Parent && ((Parent) node).layoutFlag != LayoutFlags.CLEAN)) {
relayout = true;
}
node.setParent(Parent.this);
node.setScenes(getScene(), getSubScene(), /* reapplyCSS*/ true);
*** 353,362 ****
--- 440,453 ----
impl_markDirty(DirtyBits.PARENT_CHILDREN);
// Force synchronization to include the handling of invisible node
// so that removed list will get cleanup to prevent memory leak.
impl_markDirty(DirtyBits.NODE_FORCE_SYNC);
+
+ if(sortedChildrenDirty) {
+ impl_markDirty(DirtyBits.NODE_PRIORITY_ORDER);
+ }
}
}) {
@Override
protected void onProposedChange(final List<Node> newNodes, int[] toBeRemoved) {
*** 693,719 ****
final Node node = children.get(i);
node.computeDerivedDepthTest();
}
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended for use and will be removed in the next version
*/
@Deprecated
@Override protected void impl_pickNodeLocal(PickRay pickRay, PickResultChooser result) {
-
double boundsDistance = impl_intersectsBounds(pickRay);
! if (!Double.isNaN(boundsDistance)) {
! for (int i = children.size()-1; i >= 0; i--) {
! children.get(i).impl_pickNode(pickRay, result);
! if (result.isClosed()) {
! return;
! }
! }
!
if (isPickOnBounds()) {
result.offer(this, boundsDistance, PickResultChooser.computePoint(pickRay, boundsDistance));
}
}
}
--- 784,813 ----
final Node node = children.get(i);
node.computeDerivedDepthTest();
}
}
+ boolean pickChildrenNode(PickRay pickRay, PickResultChooser result) {
+ List<Node> orderedChildren = getOrderedChildren();
+ for (int i = orderedChildren.size() - 1; i >= 0; i--) {
+ orderedChildren.get(i).impl_pickNode(pickRay, result);
+ if (result.isClosed()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended for use and will be removed in the next version
*/
@Deprecated
@Override protected void impl_pickNodeLocal(PickRay pickRay, PickResultChooser result) {
double boundsDistance = impl_intersectsBounds(pickRay);
! if (!Double.isNaN(boundsDistance) && pickChildrenNode(pickRay, result)) {
if (isPickOnBounds()) {
result.offer(this, boundsDistance, PickResultChooser.computePoint(pickRay, boundsDistance));
}
}
}