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)); } } }