11using System ;
2- using System . Collections ;
32using System . Collections . Generic ;
43using System . Linq ;
54using UnityEditor ;
65using UnityEngine ;
7- using UnityEngine . Profiling ;
86
97namespace GitHub . Unity
108{
@@ -31,8 +29,8 @@ public abstract class Tree: ITree
3129
3230 [ SerializeField ] private List < TreeNode > nodes = new List < TreeNode > ( ) ;
3331 [ SerializeField ] private TreeNode selectedNode = null ;
34- [ SerializeField ] private TreeNode activeNode = null ;
3532 [ SerializeField ] private TreeNodeDictionary folders = new TreeNodeDictionary ( ) ;
33+ [ SerializeField ] private TreeNodeDictionary checkedFileNodes = new TreeNodeDictionary ( ) ;
3634
3735 [ NonSerialized ] private Stack < bool > indents = new Stack < bool > ( ) ;
3836 [ NonSerialized ] private Action < TreeNode > rightClickNextRender ;
@@ -101,11 +99,6 @@ public void AddNode(string path, string label, int level, bool isFolder, bool is
10199 SetNodeIcon ( node ) ;
102100 nodes . Add ( node ) ;
103101
104- if ( isActive )
105- {
106- activeNode = node ;
107- }
108-
109102 if ( isSelected )
110103 {
111104 SelectedNode = node ;
@@ -120,14 +113,19 @@ public void AddNode(string path, string label, int level, bool isFolder, bool is
120113 public void Clear ( )
121114 {
122115 folders . Clear ( ) ;
116+ checkedFileNodes . Clear ( ) ;
123117 nodes . Clear ( ) ;
124118 SelectedNode = null ;
125119 }
126120
127- public HashSet < string > GetCollapsedFolders ( )
121+ public IEnumerable < string > GetCollapsedFolders ( )
128122 {
129- var collapsedFoldersEnumerable = folders . Where ( pair => pair . Value . IsCollapsed ) . Select ( pair => pair . Key ) ;
130- return new HashSet < string > ( collapsedFoldersEnumerable ) ;
123+ return folders . Where ( pair => pair . Value . IsCollapsed ) . Select ( pair => pair . Key ) ;
124+ }
125+
126+ public IEnumerable < string > GetCheckedFiles ( )
127+ {
128+ return checkedFileNodes . Where ( pair => pair . Value . CheckState == CheckState . Checked ) . Select ( pair => pair . Key ) ;
131129 }
132130
133131 public Rect Render ( Rect containingRect , Rect rect , Vector2 scroll , Action < TreeNode > singleClick = null , Action < TreeNode > doubleClick = null , Action < TreeNode > rightClick = null )
@@ -167,7 +165,7 @@ public Rect Render(Rect containingRect, Rect rect, Vector2 scroll, Action<TreeNo
167165 }
168166 else if ( renderResult == TreeNodeRenderResult . CheckChange )
169167 {
170- ToggleNodeCheck ( 0 , titleNode ) ;
168+ ToggleNodeChecked ( 0 , titleNode ) ;
171169 }
172170
173171 RequiresRepaint = HandleInput ( rect , titleNode , 0 ) ;
@@ -200,7 +198,7 @@ public Rect Render(Rect containingRect, Rect rect, Vector2 scroll, Action<TreeNo
200198 }
201199 else if ( renderResult == TreeNodeRenderResult . CheckChange )
202200 {
203- ToggleNodeCheck ( i , node ) ;
201+ ToggleNodeChecked ( i , node ) ;
204202 }
205203
206204 if ( node . Level < level )
@@ -257,26 +255,134 @@ public void Blur()
257255 RequiresRepaint = true ;
258256 }
259257
260- private void ToggleNodeCheck ( int idx , TreeNode node )
258+ private void ToggleNodeChecked ( int idx , TreeNode node )
261259 {
260+ var isChecked = false ;
261+
262+ switch ( node . CheckState )
263+ {
264+ case CheckState . Mixed :
265+ case CheckState . Empty :
266+ node . CheckState = CheckState . Checked ;
267+ isChecked = true ;
268+ break ;
269+
270+ case CheckState . Checked :
271+ node . CheckState = CheckState . Empty ;
272+ break ;
273+ }
274+
262275 if ( node . IsFolder )
263276 {
264-
277+ ToggleChildrenChecked ( idx , node , isChecked ) ;
265278 }
266279 else
267280 {
268- switch ( node . CheckState )
281+ if ( isChecked )
282+ {
283+ checkedFileNodes . Add ( node . Path , node ) ;
284+ }
285+ else
286+ {
287+ checkedFileNodes . Remove ( node . Path ) ;
288+ }
289+ }
290+
291+ ToggleParentFoldersChecked ( idx , node , isChecked ) ;
292+ }
293+
294+ private void ToggleChildrenChecked ( int idx , TreeNode node , bool isChecked )
295+ {
296+ for ( var i = idx + 1 ; i < nodes . Count && node . Level < nodes [ i ] . Level ; i ++ )
297+ {
298+ var childNode = nodes [ i ] ;
299+ var wasChecked = childNode . CheckState == CheckState . Checked ;
300+ childNode . CheckState = isChecked ? CheckState . Checked : CheckState . Empty ;
301+
302+ if ( childNode . IsFolder )
303+ {
304+ ToggleChildrenChecked ( i , childNode , isChecked ) ;
305+ }
306+ else
307+ {
308+ if ( isChecked && ! wasChecked )
309+ {
310+ checkedFileNodes . Add ( childNode . Path , childNode ) ;
311+ }
312+ else if ( ! isChecked && wasChecked )
313+ {
314+ checkedFileNodes . Remove ( childNode . Path ) ;
315+ }
316+ }
317+ }
318+ }
319+
320+ private void ToggleParentFoldersChecked ( int idx , TreeNode node , bool isChecked )
321+ {
322+ while ( true )
323+ {
324+ if ( node . Level > 0 )
269325 {
270- case CheckState . Empty :
271- node . CheckState = CheckState . Checked ;
272- break ;
326+ var siblingsInSameState = true ;
327+ var firstSiblingIndex = idx ;
328+
329+ for ( var i = idx - 1 ; i > 0 && node . Level <= nodes [ i ] . Level ; i -- )
330+ {
331+ var previousNode = nodes [ i ] ;
332+ if ( node . Level < previousNode . Level )
333+ {
334+ continue ;
335+ }
336+
337+ firstSiblingIndex = i ;
338+
339+ if ( siblingsInSameState )
340+ {
341+ var previousNodeIsChecked = previousNode . CheckState == CheckState . Checked ;
342+
343+ if ( isChecked != previousNodeIsChecked )
344+ {
345+ siblingsInSameState = false ;
346+ }
347+ }
348+ }
349+
350+ if ( siblingsInSameState )
351+ {
352+ for ( var i = idx + 1 ; i < nodes . Count && node . Level <= nodes [ i ] . Level ; i ++ )
353+ {
354+ var followingNode = nodes [ i ] ;
355+ if ( node . Level < followingNode . Level )
356+ {
357+ continue ;
358+ }
359+
360+ var followingNodeIsChecked = followingNode . CheckState == CheckState . Checked ;
361+ if ( isChecked != followingNodeIsChecked )
362+ {
363+ siblingsInSameState = false ;
364+ break ;
365+ }
366+ }
367+ }
273368
274- case CheckState . Checked :
275- node . CheckState = CheckState . Empty ;
276- break ;
369+ var parentIndex = firstSiblingIndex - 1 ;
370+ var parentNode = nodes [ parentIndex ] ;
371+ if ( siblingsInSameState )
372+ {
373+ parentNode . CheckState = isChecked ? CheckState . Checked : CheckState . Empty ;
374+ }
375+ else
376+ {
377+ parentNode . CheckState = CheckState . Mixed ;
378+ }
379+
380+ idx = parentIndex ;
381+ node = parentNode ;
382+ continue ;
277383 }
278384
279- Debug . LogFormat ( "Ripple CheckState index:{0} level:{1}" , idx , node . Level ) ;
385+ break ;
280386 }
281387 }
282388
@@ -303,13 +409,13 @@ private void ToggleNodeVisibility(int idx, TreeNode node)
303409
304410 private bool HandleInput ( Rect rect , TreeNode currentNode , int index , Action < TreeNode > singleClick = null , Action < TreeNode > doubleClick = null , Action < TreeNode > rightClick = null )
305411 {
306- bool selectionChanged = false ;
412+ var requiresRepaint = false ;
307413 var clickRect = new Rect ( 0f , rect . y , rect . width , rect . height ) ;
308414 if ( Event . current . type == EventType . MouseDown && clickRect . Contains ( Event . current . mousePosition ) )
309415 {
310416 Event . current . Use ( ) ;
311417 SelectedNode = currentNode ;
312- selectionChanged = true ;
418+ requiresRepaint = true ;
313419 var clickCount = Event . current . clickCount ;
314420 var mouseButton = Event . current . button ;
315421
@@ -335,41 +441,50 @@ private bool HandleInput(Rect rect, TreeNode currentNode, int index, Action<Tree
335441 int directionX = Event . current . keyCode == KeyCode . LeftArrow ? - 1 : Event . current . keyCode == KeyCode . RightArrow ? 1 : 0 ;
336442 if ( directionY != 0 || directionX != 0 )
337443 {
444+ Event . current . Use ( ) ;
445+
338446 if ( directionY > 0 )
339447 {
340- selectionChanged = SelectNext ( index , false ) != index ;
448+ requiresRepaint = SelectNext ( index , false ) != index ;
341449 }
342450 else if ( directionY < 0 )
343451 {
344- selectionChanged = SelectPrevious ( index , false ) != index ;
452+ requiresRepaint = SelectPrevious ( index , false ) != index ;
345453 }
346454 else if ( directionX > 0 )
347455 {
348456 if ( currentNode . IsFolder && currentNode . IsCollapsed )
349457 {
350458 ToggleNodeVisibility ( index , currentNode ) ;
351- Event . current . Use ( ) ;
352459 }
353460 else
354461 {
355- selectionChanged = SelectNext ( index , true ) != index ;
462+ requiresRepaint = SelectNext ( index , true ) != index ;
356463 }
357464 }
358465 else if ( directionX < 0 )
359466 {
360467 if ( currentNode . IsFolder && ! currentNode . IsCollapsed )
361468 {
362469 ToggleNodeVisibility ( index , currentNode ) ;
363- Event . current . Use ( ) ;
364470 }
365471 else
366472 {
367- selectionChanged = SelectPrevious ( index , true ) != index ;
473+ requiresRepaint = SelectPrevious ( index , true ) != index ;
368474 }
369475 }
370476 }
477+
478+ if ( IsCheckable && Event . current . keyCode == KeyCode . Space )
479+ {
480+ Event . current . Use ( ) ;
481+
482+ ToggleNodeChecked ( index , currentNode ) ;
483+ requiresRepaint = true ;
484+ }
371485 }
372- return selectionChanged ;
486+
487+ return requiresRepaint ;
373488 }
374489
375490 private int SelectNext ( int index , bool foldersOnly )
0 commit comments