void AddExpandedChildrenRecursively(TreeViewItem item, ExpandedMarkerIdHierarchy expandedHierarchy) { if (item.children == null) { return; } for (var i = 0; i < item.children.Count; ++i) { var childItem = item.children[i]; // Inlining !IsChildListForACollapsedParent without childList.Count == 1 check, as we only create list if we have children if (childItem.children != null && childItem.children[0] != null) { var subHierarchy = new ExpandedMarkerIdHierarchy(); if (expandedHierarchy.expandedMarkers == null) { expandedHierarchy.expandedMarkers = new Dictionary <int, ExpandedMarkerIdHierarchy>(); } try { expandedHierarchy.expandedMarkers.Add(m_FrameDataView.GetItemMarkerID(childItem.id), subHierarchy); } catch (ArgumentException) { } AddExpandedChildrenRecursively(childItem, subHierarchy); } } }
void StoreExpandedState() { if (m_ExpandedMarkersHierarchy != null) { return; } if (m_FrameDataView == null || !m_FrameDataView.IsValid()) { return; } m_ExpandedMarkersHierarchy = new ExpandedMarkerIdHierarchy(); AddExpandedChildrenRecursively(rootItem, m_ExpandedMarkersHierarchy); }
protected override void ExpandedStateChanged() { // Invalidate saved expanded state if user altered current state. m_ExpandedMarkersHierarchy = null; }
void AddAllChildren(FrameDataTreeViewItem parent, ExpandedMarkerIdHierarchy parentExpandedHierararchy, IList <TreeViewItem> newRows, List <int> newExpandedIds) { m_ReusableVisitList.AddFirst(AcquireTreeTraversalStateNode(parent, parentExpandedHierararchy)); // Depth-first traversal. // Think of it as an unrolled recursion where stack state is defined by TreeTraversalState. while (m_ReusableVisitList.First != null) { var currentItem = m_ReusableVisitList.First.Value; m_TreeTraversalStatePool.Push(m_ReusableVisitList.First); m_ReusableVisitList.RemoveFirst(); if (currentItem.item.depth != -1) { newRows.Add(currentItem.item); } m_FrameDataView.GetItemChildren(currentItem.item.id, m_ReusableChildrenIds); var childrenCount = m_ReusableChildrenIds.Count; if (childrenCount == 0) { continue; } if (currentItem.item.depth != -1) { // Check expansion state from a previous frame view state (marker id path) or current tree view state (frame-specific id). bool needsExpansion; if (m_ExpandedMarkersHierarchy == null) { // When we alter expansion state of the currently selected frame, // we rely on TreeView's IsExpanded functionality. needsExpansion = IsExpanded(currentItem.item.id); } else { // When we switch to another frame, we rebuild expanded state based on stored m_ExpandedMarkersHierarchy // which represents tree of expanded nodes. needsExpansion = currentItem.expandedHierarchy != null; } if (!needsExpansion) { if (currentItem.item.children == null) { currentItem.item.children = CreateChildListForCollapsedParent(); } continue; } if (newExpandedIds != null) { newExpandedIds.Add(currentItem.item.id); } } // Generate children based on the view data. if (currentItem.item.children == null) { // Reuse existing list. if (m_ChildrenPool.Count > 0) { currentItem.item.children = m_ChildrenPool.Pop(); } else { currentItem.item.children = new List <TreeViewItem>(); } } currentItem.item.children.Clear(); currentItem.item.children.Capacity = childrenCount; for (var i = 0; i < childrenCount; ++i) { var child = AcquireFrameDataTreeViewItem(m_FrameDataView, m_ReusableChildrenIds[i], currentItem.item.depth + 1, currentItem.item); currentItem.item.children.Add(child); } // Add children to the traversal list. // We add all of them in front, so it is depth search, but with preserved siblings order. LinkedListNode <TreeTraversalState> prev = null; foreach (var child in currentItem.item.children) { var childMarkerId = m_FrameDataView.GetItemMarkerID(child.id); ExpandedMarkerIdHierarchy childExpandedHierarchy = null; if (currentItem.expandedHierarchy != null && currentItem.expandedHierarchy.expandedMarkers != null) { currentItem.expandedHierarchy.expandedMarkers.TryGetValue(childMarkerId, out childExpandedHierarchy); } var traversalState = AcquireTreeTraversalStateNode((FrameDataTreeViewItem)child, childExpandedHierarchy); if (prev == null) { m_ReusableVisitList.AddFirst(traversalState); } else { m_ReusableVisitList.AddAfter(prev, traversalState); } prev = traversalState; } } if (newExpandedIds != null) { newExpandedIds.Sort(); } }
LinkedListNode <TreeTraversalState> AcquireTreeTraversalStateNode(FrameDataTreeViewItem item, ExpandedMarkerIdHierarchy expandedHierarchy) { if (m_TreeTraversalStatePool.Count == 0) { return(new LinkedListNode <TreeTraversalState>(new TreeTraversalState() { item = item, expandedHierarchy = expandedHierarchy })); } var node = m_TreeTraversalStatePool.Pop(); node.Value = new TreeTraversalState() { item = item, expandedHierarchy = expandedHierarchy }; return(node); }
void AddAllChildren(FrameDataTreeViewItem parent, ExpandedMarkerIdHierarchy parentExpandedHierararchy, IList <TreeViewItem> newRows, List <int> newExpandedIds) { m_ReusableVisitList.AddFirst(AcquireTreeTraversalStateNode(parent, parentExpandedHierararchy)); // Depth-first traversal. // Think of it as an unrolled recursion where stack state is defined by TreeTraversalState. while (m_ReusableVisitList.First != null) { var currentItem = m_ReusableVisitList.First.Value.item; var currentExpandedHierarchy = m_ReusableVisitList.First.Value.expandedHierarchy; { // scope firstItem to avoid reusing it accidentally var firstItem = m_ReusableVisitList.First; firstItem.Value = default; m_TreeTraversalStatePool.Push(firstItem); m_ReusableVisitList.RemoveFirst(); } // This loop can be a bit of a hot path so, micro optimizations like property caching and inlinig have some effect: // So... cache depth var currentItemDepth = currentItem.depth; // and cache children, remember to also set currentItem.children if a new list is assigned var currentItemChildren = currentItem.children; if (currentItemDepth != ProfilerFrameDataHierarchyView.invalidTreeViewDepth) { newRows.Add(currentItem); } m_FrameDataView.GetItemChildren(currentItem.id, m_ReusableChildrenIds); var childrenCount = m_ReusableChildrenIds.Count; if (childrenCount == 0) { continue; } if (currentItemDepth != ProfilerFrameDataHierarchyView.invalidTreeViewDepth) { // Check expansion state from a previous frame view state (marker id path) or current tree view state (frame-specific id). bool needsExpansion; if (m_ExpandedMarkersHierarchy == null) { // When we alter expansion state of the currently selected frame, // we rely on TreeView's IsExpanded functionality. needsExpansion = IsExpanded(currentItem.id); } else { // When we switch to another frame, we rebuild expanded state based on stored m_ExpandedMarkersHierarchy // which represents tree of expanded nodes. needsExpansion = currentExpandedHierarchy != null; } if (!needsExpansion) { if (currentItemChildren == null) { // Lets create a fake laze child list. // Not using CreateChildListForCollapsedParent as that list returns a static list and will cause funkyness later on. // We have a pool, so we're gonna use that. if (m_ChildrenPool.Count > 0) { currentItem.children = currentItemChildren = m_ChildrenPool.Pop(); currentItemChildren.Clear(); } else { currentItem.children = currentItemChildren = new List <TreeViewItem>(); } currentItemChildren.Add(null); } continue; } if (newExpandedIds != null) { newExpandedIds.Add(currentItem.id); } } // Generate children based on the view data. if (currentItemChildren == null) { // Reuse existing list. if (m_ChildrenPool.Count > 0) { currentItem.children = currentItemChildren = m_ChildrenPool.Pop(); } else { currentItem.children = currentItemChildren = new List <TreeViewItem>(); } } currentItemChildren.Clear(); const int k_MaxSuperflousCapacity = 32 - 1; // only reset capacity if needed or if too much space would be wasted. Otherwise, what's the point of pooling them? if (currentItemChildren.Capacity < childrenCount || currentItemChildren.Capacity > childrenCount + k_MaxSuperflousCapacity) { currentItemChildren.Capacity = childrenCount; } for (var i = 0; i < childrenCount; ++i) { var child = AcquireFrameDataTreeViewItem(m_FrameDataView, m_ReusableChildrenIds[i], currentItemDepth + 1, currentItem); currentItemChildren.Add(child); } // Add children to the traversal list. // We add all of them in front, so it is depth search, but with preserved siblings order. LinkedListNode <TreeTraversalState> prev = null; foreach (var child in currentItemChildren) { var childMarkerId = m_FrameDataView.GetItemMarkerID(child.id); ExpandedMarkerIdHierarchy childExpandedHierarchy = null; if (currentExpandedHierarchy != null && currentExpandedHierarchy.expandedMarkers != null) { currentExpandedHierarchy.expandedMarkers.TryGetValue(childMarkerId, out childExpandedHierarchy); } var traversalState = AcquireTreeTraversalStateNode((FrameDataTreeViewItem)child, childExpandedHierarchy); if (prev == null) { m_ReusableVisitList.AddFirst(traversalState); } else { m_ReusableVisitList.AddAfter(prev, traversalState); } prev = traversalState; } } if (newExpandedIds != null) { newExpandedIds.Sort(); } }