22// Heap Explorer for Unity. Copyright (c) 2019-2020 Peter Schraut (www.console-dev.de). See LICENSE.md
33// https://github.com/pschraut/UnityHeapExplorer/
44//
5- using System . Collections ;
5+
6+ using System ;
67using System . Collections . Generic ;
78using UnityEngine ;
89using UnityEditor . IMGUI . Controls ;
@@ -31,12 +32,11 @@ public HeapExplorerWindow window
3132 SearchTextParser . Result m_Search = new SearchTextParser . Result ( ) ;
3233 protected string m_EditorPrefsKey ;
3334 int m_FirstVisibleRow ;
35+ private List < TreeViewItem > m_RowsCache ;
3436 IList < int > m_Expanded = new List < int > ( 32 ) ;
3537 TreeViewItem m_Tree ;
36- TreeViewItem m_ActiveTree ;
3738 string [ ] m_SearchCache = new string [ 32 ] ;
3839 System . Text . StringBuilder m_SearchBuilder = new System . Text . StringBuilder ( ) ;
39- bool m_HasSearch ;
4040
4141 public AbstractTreeView ( HeapExplorerWindow window , string editorPrefsKey , TreeViewState state )
4242 : base ( state )
@@ -73,7 +73,6 @@ public AbstractTreeView(HeapExplorerWindow window, string editorPrefsKey, TreeVi
7373 public void SetTree ( TreeViewItem tree )
7474 {
7575 m_Tree = tree ;
76- m_ActiveTree = m_Tree ;
7776 Reload ( ) ;
7877 }
7978
@@ -84,107 +83,109 @@ protected override bool CanMultiSelect(TreeViewItem item)
8483
8584 protected override TreeViewItem BuildRoot ( )
8685 {
87- if ( m_ActiveTree != null )
88- return m_ActiveTree ;
86+ if ( m_Tree != null )
87+ return m_Tree ;
8988
9089 var root = new TreeViewItem { id = 0 , depth = - 1 , displayName = "Root" } ;
9190 root . AddChild ( new TreeViewItem { id = root . id + 1 , depth = - 1 , displayName = "" } ) ;
9291 return root ;
9392 }
9493
95- void OnSortingChanged ( MultiColumnHeader multiColumnHeader )
94+ protected override IList < TreeViewItem > BuildRows ( TreeViewItem root )
9695 {
97- if ( rootItem == null || ! rootItem . hasChildren )
98- return ;
96+ if ( m_RowsCache == null )
97+ m_RowsCache = new List < TreeViewItem > ( 128 ) ;
98+ m_RowsCache . Clear ( ) ;
9999
100- SortItemsRecursive ( m_ActiveTree , OnSortItem ) ;
101- Reload ( ) ;
102- }
100+ if ( hasSearch )
101+ {
102+ SearchTree ( root , searchString , m_RowsCache ) ;
103+ m_RowsCache . Sort ( CompareItem ) ;
104+ }
105+ else
106+ {
107+ SortAndAddExpandedRows ( root , m_RowsCache ) ;
108+ }
103109
104- protected abstract int OnSortItem ( TreeViewItem x , TreeViewItem y ) ;
110+ return m_RowsCache ;
111+ }
105112
106- protected void SortItemsRecursive ( TreeViewItem parent , System . Comparison < TreeViewItem > comparison )
113+ protected virtual void SearchTree ( TreeViewItem root , string search , List < TreeViewItem > result )
107114 {
108- if ( parent == null )
109- return ;
115+ var stack = new Stack < TreeViewItem > ( ) ;
116+ stack . Push ( root ) ;
117+ while ( stack . Count > 0 )
118+ {
119+ TreeViewItem current = stack . Pop ( ) ;
120+ if ( current . children != null )
121+ {
122+ foreach ( var child in current . children )
123+ {
124+ if ( child != null )
125+ {
126+ if ( DoesItemMatchSearch ( child , search ) )
127+ result . Add ( child ) ;
110128
111- var sortMe = new List < TreeViewItem > ( ) ;
112- sortMe . Add ( parent ) ;
129+ stack . Push ( child ) ;
130+ }
131+ }
132+ }
133+ }
134+ }
113135
114- for ( var n = 0 ; n < sortMe . Count ; ++ n )
136+ protected virtual void SortAndAddExpandedRows ( TreeViewItem root , IList < TreeViewItem > rows )
137+ {
138+ if ( root . hasChildren )
115139 {
116- var item = sortMe [ n ] ;
117- if ( item . hasChildren )
140+ root . children . Sort ( CompareItem ) ;
141+ foreach ( TreeViewItem child in root . children )
118142 {
119- item . children . Sort ( comparison ) ;
120- sortMe . AddRange ( item . children ) ; // sort items of children too (kind of recursive)
143+ GetAndSortExpandedRowsRecursive ( child , rows ) ;
121144 }
122145 }
123146 }
124147
125- public void Search ( string search )
148+ void GetAndSortExpandedRowsRecursive ( TreeViewItem item , IList < TreeViewItem > expandedRows )
126149 {
127- m_HasSearch = ! string . IsNullOrEmpty ( search ) ;
128-
129- var selectedId = - 1 ;
130- var selection = new List < int > ( this . GetSelection ( ) ) ;
131- if ( selection != null && selection . Count != 0 )
132- selectedId = selection [ 0 ] ;
133- selection . Clear ( ) ;
134-
135- m_Search = SearchTextParser . Parse ( search ) ;
150+ if ( item == null )
151+ Debug . LogError ( "Found a TreeViewItem that is null. Invalid use of AddExpandedRows(): This method is only valid to call if you have built the full tree of TreeViewItems." ) ;
136152
137- // I don't use the TreeViewControl base.searchString API, because I didn't get
138- // it to work to display filtered content sorted.
139- // Therefore I perform the filter myself as seen below, by building a new tree
140- // containing a flat list of filtered items only.
141- //base.searchString = search;
153+ expandedRows . Add ( item ) ;
142154
143- if ( ! m_HasSearch )
144- {
145- m_ActiveTree = m_Tree ;
146- }
147- else
155+ if ( item . hasChildren && IsExpanded ( item . id ) )
148156 {
149- // Create a node that contains all items that match the search
150- m_ActiveTree = new TreeViewItem { id = 0 , depth = - 1 , displayName = "Root" } ;
151-
152- // Get all items in the tree as a flat list
153- var itemsToProcess = new List < TreeViewItem > ( ) ;
154- itemsToProcess . Add ( m_Tree ) ;
155- for ( var n = 0 ; n < itemsToProcess . Count ; ++ n )
157+ item . children . Sort ( CompareItem ) ;
158+ foreach ( TreeViewItem child in item . children )
156159 {
157- var item = itemsToProcess [ n ] ;
158- if ( item == null )
159- continue ;
160-
161- if ( item . hasChildren )
162- itemsToProcess . AddRange ( item . children ) ; // process all children too (this makes it kind of recursive)
160+ GetAndSortExpandedRowsRecursive ( child , expandedRows ) ;
161+ }
162+ }
163+ }
163164
164- if ( item . id == 0 )
165- continue ; // ignore tree root node itself
165+ void OnSortingChanged ( MultiColumnHeader multiColumnHeader )
166+ {
167+ if ( rootItem == null || ! rootItem . hasChildren )
168+ return ;
166169
167- if ( ! DoesItemMatchSearch ( item , search ) )
168- continue ;
170+ Reload ( ) ;
171+ }
169172
170- // Keep track of the selected item
171- if ( selectedId == item . id )
172- {
173- selection = new List < int > ( ) ;
174- selection . Add ( selectedId ) ;
175- }
173+ protected abstract int OnSortItem ( TreeViewItem x , TreeViewItem y ) ;
176174
177- m_ActiveTree . AddChild ( item ) ;
178- }
175+ protected int CompareItem ( TreeViewItem x , TreeViewItem y )
176+ {
177+ int result = OnSortItem ( x , y ) ;
178+ if ( result == 0 )
179+ return x . id . CompareTo ( y . id ) ;
180+ return result ;
181+ }
179182
180- // If the search didn't find any item, make sure to add at least one
181- // invisible item, otherwise the TreeViewControl displays an error.
182- if ( ! m_ActiveTree . hasChildren )
183- m_ActiveTree . AddChild ( new TreeViewItem { id = m_ActiveTree . id + 1 , depth = - 1 , displayName = "" } ) ;
184- }
183+ public void Search ( string search )
184+ {
185+ var selection = new List < int > ( this . GetSelection ( ) ) ;
185186
186- SortItemsRecursive ( m_ActiveTree , OnSortItem ) ;
187- Reload ( ) ;
187+ m_Search = SearchTextParser . Parse ( search ) ;
188+ base . searchString = search ;
188189
189190 if ( selection != null && selection . Count > 0 )
190191 this . SetSelection ( selection , TreeViewSelectionOptions . FireSelectionChanged | TreeViewSelectionOptions . RevealAndFrame ) ;
@@ -362,7 +363,7 @@ protected override void RowGUI(RowGUIArgs args)
362363 rect . width -= extraSpaceBeforeIconAndLabel ;
363364
364365 // Display the tree as a flat list when content is filtered
365- if ( m_HasSearch )
366+ if ( hasSearch )
366367 rect = TreeViewUtility . IndentByDepth ( 0 , rect ) ;
367368 else
368369 rect = TreeViewUtility . IndentByDepth ( args . item . depth , rect ) ;
0 commit comments