1
1
using System ;
2
- using System . Collections ;
3
2
using System . Collections . Generic ;
4
3
using System . Linq ;
5
4
using UnityEditor ;
6
5
using UnityEngine ;
7
- using UnityEngine . Profiling ;
8
6
9
7
namespace GitHub . Unity
10
8
{
@@ -31,8 +29,8 @@ public abstract class Tree: ITree
31
29
32
30
[ SerializeField ] private List < TreeNode > nodes = new List < TreeNode > ( ) ;
33
31
[ SerializeField ] private TreeNode selectedNode = null ;
34
- [ SerializeField ] private TreeNode activeNode = null ;
35
32
[ SerializeField ] private TreeNodeDictionary folders = new TreeNodeDictionary ( ) ;
33
+ [ SerializeField ] private TreeNodeDictionary checkedFileNodes = new TreeNodeDictionary ( ) ;
36
34
37
35
[ NonSerialized ] private Stack < bool > indents = new Stack < bool > ( ) ;
38
36
[ NonSerialized ] private Action < TreeNode > rightClickNextRender ;
@@ -101,11 +99,6 @@ public void AddNode(string path, string label, int level, bool isFolder, bool is
101
99
SetNodeIcon ( node ) ;
102
100
nodes . Add ( node ) ;
103
101
104
- if ( isActive )
105
- {
106
- activeNode = node ;
107
- }
108
-
109
102
if ( isSelected )
110
103
{
111
104
SelectedNode = node ;
@@ -120,14 +113,19 @@ public void AddNode(string path, string label, int level, bool isFolder, bool is
120
113
public void Clear ( )
121
114
{
122
115
folders . Clear ( ) ;
116
+ checkedFileNodes . Clear ( ) ;
123
117
nodes . Clear ( ) ;
124
118
SelectedNode = null ;
125
119
}
126
120
127
- public HashSet < string > GetCollapsedFolders ( )
121
+ public IEnumerable < string > GetCollapsedFolders ( )
128
122
{
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 ) ;
131
129
}
132
130
133
131
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
167
165
}
168
166
else if ( renderResult == TreeNodeRenderResult . CheckChange )
169
167
{
170
- ToggleNodeCheck ( 0 , titleNode ) ;
168
+ ToggleNodeChecked ( 0 , titleNode ) ;
171
169
}
172
170
173
171
RequiresRepaint = HandleInput ( rect , titleNode , 0 ) ;
@@ -200,7 +198,7 @@ public Rect Render(Rect containingRect, Rect rect, Vector2 scroll, Action<TreeNo
200
198
}
201
199
else if ( renderResult == TreeNodeRenderResult . CheckChange )
202
200
{
203
- ToggleNodeCheck ( i , node ) ;
201
+ ToggleNodeChecked ( i , node ) ;
204
202
}
205
203
206
204
if ( node . Level < level )
@@ -257,26 +255,134 @@ public void Blur()
257
255
RequiresRepaint = true ;
258
256
}
259
257
260
- private void ToggleNodeCheck ( int idx , TreeNode node )
258
+ private void ToggleNodeChecked ( int idx , TreeNode node )
261
259
{
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
+
262
275
if ( node . IsFolder )
263
276
{
264
-
277
+ ToggleChildrenChecked ( idx , node , isChecked ) ;
265
278
}
266
279
else
267
280
{
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 )
269
325
{
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
+ }
273
368
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 ;
277
383
}
278
384
279
- Debug . LogFormat ( "Ripple CheckState index:{0} level:{1}" , idx , node . Level ) ;
385
+ break ;
280
386
}
281
387
}
282
388
@@ -303,13 +409,13 @@ private void ToggleNodeVisibility(int idx, TreeNode node)
303
409
304
410
private bool HandleInput ( Rect rect , TreeNode currentNode , int index , Action < TreeNode > singleClick = null , Action < TreeNode > doubleClick = null , Action < TreeNode > rightClick = null )
305
411
{
306
- bool selectionChanged = false ;
412
+ var requiresRepaint = false ;
307
413
var clickRect = new Rect ( 0f , rect . y , rect . width , rect . height ) ;
308
414
if ( Event . current . type == EventType . MouseDown && clickRect . Contains ( Event . current . mousePosition ) )
309
415
{
310
416
Event . current . Use ( ) ;
311
417
SelectedNode = currentNode ;
312
- selectionChanged = true ;
418
+ requiresRepaint = true ;
313
419
var clickCount = Event . current . clickCount ;
314
420
var mouseButton = Event . current . button ;
315
421
@@ -335,41 +441,50 @@ private bool HandleInput(Rect rect, TreeNode currentNode, int index, Action<Tree
335
441
int directionX = Event . current . keyCode == KeyCode . LeftArrow ? - 1 : Event . current . keyCode == KeyCode . RightArrow ? 1 : 0 ;
336
442
if ( directionY != 0 || directionX != 0 )
337
443
{
444
+ Event . current . Use ( ) ;
445
+
338
446
if ( directionY > 0 )
339
447
{
340
- selectionChanged = SelectNext ( index , false ) != index ;
448
+ requiresRepaint = SelectNext ( index , false ) != index ;
341
449
}
342
450
else if ( directionY < 0 )
343
451
{
344
- selectionChanged = SelectPrevious ( index , false ) != index ;
452
+ requiresRepaint = SelectPrevious ( index , false ) != index ;
345
453
}
346
454
else if ( directionX > 0 )
347
455
{
348
456
if ( currentNode . IsFolder && currentNode . IsCollapsed )
349
457
{
350
458
ToggleNodeVisibility ( index , currentNode ) ;
351
- Event . current . Use ( ) ;
352
459
}
353
460
else
354
461
{
355
- selectionChanged = SelectNext ( index , true ) != index ;
462
+ requiresRepaint = SelectNext ( index , true ) != index ;
356
463
}
357
464
}
358
465
else if ( directionX < 0 )
359
466
{
360
467
if ( currentNode . IsFolder && ! currentNode . IsCollapsed )
361
468
{
362
469
ToggleNodeVisibility ( index , currentNode ) ;
363
- Event . current . Use ( ) ;
364
470
}
365
471
else
366
472
{
367
- selectionChanged = SelectPrevious ( index , true ) != index ;
473
+ requiresRepaint = SelectPrevious ( index , true ) != index ;
368
474
}
369
475
}
370
476
}
477
+
478
+ if ( IsCheckable && Event . current . keyCode == KeyCode . Space )
479
+ {
480
+ Event . current . Use ( ) ;
481
+
482
+ ToggleNodeChecked ( index , currentNode ) ;
483
+ requiresRepaint = true ;
484
+ }
371
485
}
372
- return selectionChanged ;
486
+
487
+ return requiresRepaint ;
373
488
}
374
489
375
490
private int SelectNext ( int index , bool foldersOnly )
0 commit comments