modules/graphics/src/main/java/javafx/scene/Parent.java
Print this page
*** 59,68 ****
--- 59,69 ----
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 java.util.Collections;
import javafx.stage.Window;
/**
* The base class for all nodes that have children in the scene graph.
* <p>
*** 138,147 ****
--- 139,153 ----
}
pgChildrenSize = children.size();
startIdx = pgChildrenSize;
}
+ if (sortedChildrenDirty) {
+ computeSortedChidrenAndUpdatePeer();
+ // TODO: if (Utils.assertionEnabled()) validateSortedChildren();
+ sortedChildrenDirty = false;
+ }
if (Utils.assertionEnabled()) validatePG();
}
/***********************************************************************
*** 193,202 ****
--- 199,282 ----
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 traversal order of the children
+ * is the same as the order in the children list.
+ */
+ 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 order that bigger value first (reverse ordered)
+ Collections.sort(sortedChildren, (Node a, Node b)
+ -> a.getPriorityOrder() < b.getPriorityOrder() ? 1
+ : a.getPriorityOrder() == b.getPriorityOrder() ? 0 : -1);
+
+ // System.err.println("children = " + children);
+ // System.err.println("sortedChildren = " + sortedChildren);
+ // Sort in order that bigger value first (reverse ordered)
+ // Collections.sort(peerSortedChildren, (NGNode a, NGNode b)
+ // -> a.getPriorityOrder() < b.getPriorityOrder() ? 1
+ // : a.getPriorityOrder() == b.getPriorityOrder() ? 0 : -1);
+
+ for (Node child : sortedChildren) {
+ NGNode childPeer = child.impl_getPeer();
+ peerSortedChildren.add(childPeer);
+ }
+
+ // List<NGNode> peerChildren = peer.getChildren();
+ // System.err.println("peerChildren = " + peerChildren);
+ // System.err.println("peerSortedChildren = " + peerSortedChildren);
+ // for (NGNode ng:peerSortedChildren) {
+ // System.err.println("NGNode = " + ng + ", ng's priorityOrder = " + ng.getPriorityOrder());
+ // }
+ }
+
+ }
+
+ // Call this list if children traversal order is important such as in case of
+ // rendering and picking.
+ // This should be treated as a read only list; caller shouldn't modify the return list.
+ 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 ****
--- 337,359 ----
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 ****
--- 443,456 ----
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));
}
}
}
--- 787,822 ----
final Node node = children.get(i);
node.computeDerivedDepthTest();
}
}
+ // TODO: No more adding impl_XXX method use accessor pattern instead.
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended for use and will be removed in the next version
*/
@Deprecated
! protected boolean impl_pickChildrenNode(PickRay pickRay, PickResultChooser result) {
! List<Node> sChildren = getOrderedChildren();
! for (int i = sChildren.size()-1; i >= 0; i--) {
! sChildren.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) && impl_pickChildrenNode(pickRay, result)) {
if (isPickOnBounds()) {
result.offer(this, boundsDistance, PickResultChooser.computePoint(pickRay, boundsDistance));
}
}
}