@@ -20,6 +20,10 @@ public abstract class Tree
20
20
[ SerializeField ] public Rect Margin = new Rect ( ) ;
21
21
[ SerializeField ] public Rect Padding = new Rect ( ) ;
22
22
23
+ [ SerializeField ] public string PathIgnoreRoot ;
24
+ [ SerializeField ] public string PathSeparator = "/" ;
25
+ [ SerializeField ] public bool DisplayRootNode = true ;
26
+ [ SerializeField ] public bool Checkable = false ;
23
27
[ SerializeField ] public GUIStyle FolderStyle ;
24
28
[ SerializeField ] public GUIStyle TreeNodeStyle ;
25
29
[ SerializeField ] public GUIStyle ActiveTreeNodeStyle ;
@@ -58,12 +62,15 @@ public void Load(IEnumerable<ITreeData> data, string title)
58
62
folders . Clear ( ) ;
59
63
nodes . Clear ( ) ;
60
64
65
+ var displayRootLevel = DisplayRootNode ? 1 : 0 ;
66
+
61
67
var titleNode = new TreeNode ( )
62
68
{
63
69
Name = title ,
64
70
Label = title ,
65
- Level = 0 ,
66
- IsFolder = true
71
+ Level = - 1 + displayRootLevel ,
72
+ IsFolder = true ,
73
+ Checkable = Checkable
67
74
} ;
68
75
SetNodeIcon ( titleNode ) ;
69
76
nodes . Add ( titleNode ) ;
@@ -73,12 +80,22 @@ public void Load(IEnumerable<ITreeData> data, string title)
73
80
74
81
foreach ( var d in data )
75
82
{
76
- var parts = d . Name . Split ( '/' ) ;
83
+ var fullName = d . Name ;
84
+ if ( PathIgnoreRoot != null )
85
+ {
86
+ var indexOf = fullName . IndexOf ( PathIgnoreRoot ) ;
87
+ if ( indexOf != - 1 )
88
+ {
89
+ fullName = fullName . Substring ( indexOf + PathIgnoreRoot . Length ) ;
90
+ }
91
+ }
92
+
93
+ var parts = fullName . Split ( new [ ] { PathSeparator } , StringSplitOptions . None ) ;
77
94
for ( int i = 0 ; i < parts . Length ; i ++ )
78
95
{
79
96
var label = parts [ i ] ;
80
97
var level = i + 1 ;
81
- var name = String . Join ( "/" , parts , 0 , level ) ;
98
+ var name = String . Join ( PathSeparator , parts , 0 , level ) ;
82
99
var isFolder = i < parts . Length - 1 ;
83
100
var alreadyExists = folders . ContainsKey ( name ) ;
84
101
if ( ! alreadyExists )
@@ -88,8 +105,9 @@ public void Load(IEnumerable<ITreeData> data, string title)
88
105
Name = name ,
89
106
IsActive = d . IsActive ,
90
107
Label = label ,
91
- Level = level ,
92
- IsFolder = isFolder
108
+ Level = i + displayRootLevel ,
109
+ IsFolder = isFolder ,
110
+ Checkable = Checkable
93
111
} ;
94
112
95
113
if ( node . IsActive )
@@ -134,27 +152,32 @@ public void Load(IEnumerable<ITreeData> data, string title)
134
152
135
153
public Rect Render ( Rect rect , Vector2 scroll , Action < TreeNode > singleClick = null , Action < TreeNode > doubleClick = null , Action < TreeNode > rightClick = null )
136
154
{
137
- Profiler . BeginSample ( "TreeControl" ) ;
138
- bool visible = true ;
139
- var availableHeight = rect . y + rect . height ;
140
-
141
155
RequiresRepaint = false ;
142
156
rect = new Rect ( 0f , rect . y , rect . width , ItemHeight ) ;
143
157
144
- var titleNode = nodes [ 0 ] ;
145
- bool selectionChanged = titleNode . Render ( rect , 0f , selectedNode == titleNode , FolderStyle , TreeNodeStyle , ActiveTreeNodeStyle ) ;
158
+ var level = 0 ;
146
159
147
- if ( selectionChanged )
160
+ if ( DisplayRootNode )
148
161
{
149
- ToggleNodeVisibility ( 0 , titleNode ) ;
150
- }
162
+ var titleNode = nodes [ 0 ] ;
163
+ var renderResult = titleNode . Render ( rect , Styles . TreeIndentation , selectedNode == titleNode , FolderStyle , TreeNodeStyle , ActiveTreeNodeStyle ) ;
164
+
165
+ if ( renderResult == TreeNodeRenderResult . VisibilityChange )
166
+ {
167
+ ToggleNodeVisibility ( 0 , titleNode ) ;
168
+ }
169
+ else if ( renderResult == TreeNodeRenderResult . CheckChange )
170
+ {
171
+ ToggleNodeCheck ( 0 , titleNode ) ;
172
+ }
151
173
152
- RequiresRepaint = HandleInput ( rect , titleNode , 0 ) ;
153
- rect . y += ItemHeight + ItemSpacing ;
174
+ RequiresRepaint = HandleInput ( rect , titleNode , 0 ) ;
175
+ rect . y += ItemHeight + ItemSpacing ;
154
176
155
- Indent ( ) ;
177
+ Indent ( ) ;
178
+ level = 1 ;
179
+ }
156
180
157
- int level = 1 ;
158
181
int i = 1 ;
159
182
for ( ; i < nodes . Count ; i ++ )
160
183
{
@@ -163,16 +186,15 @@ public Rect Render(Rect rect, Vector2 scroll, Action<TreeNode> singleClick = nul
163
186
{
164
187
Indent ( ) ;
165
188
}
189
+ var renderResult = node . Render ( rect , Styles . TreeIndentation , selectedNode == node , FolderStyle , TreeNodeStyle , ActiveTreeNodeStyle ) ;
166
190
167
- if ( visible )
191
+ if ( renderResult == TreeNodeRenderResult . VisibilityChange )
168
192
{
169
- var changed = node . Render ( rect , Styles . TreeIndentation , selectedNode == node , FolderStyle , TreeNodeStyle , ActiveTreeNodeStyle ) ;
170
-
171
- if ( node . IsFolder && changed )
172
- {
173
- // toggle visibility for all the nodes under this one
174
- ToggleNodeVisibility ( i , node ) ;
175
- }
193
+ ToggleNodeVisibility ( i , node ) ;
194
+ }
195
+ else if ( renderResult == TreeNodeRenderResult . CheckChange )
196
+ {
197
+ ToggleNodeCheck ( i , node ) ;
176
198
}
177
199
178
200
if ( node . Level < level )
@@ -186,17 +208,16 @@ public Rect Render(Rect rect, Vector2 scroll, Action<TreeNode> singleClick = nul
186
208
187
209
if ( ! node . IsHidden )
188
210
{
189
- if ( visible )
190
- {
191
- RequiresRepaint = HandleInput ( rect , node , i , singleClick , doubleClick , rightClick ) ;
192
- }
211
+ RequiresRepaint = HandleInput ( rect , node , i , singleClick , doubleClick , rightClick ) ;
193
212
rect . y += ItemHeight + ItemSpacing ;
194
213
}
195
214
}
196
215
197
- Unindent ( ) ;
216
+ if ( DisplayRootNode )
217
+ {
218
+ Unindent ( ) ;
219
+ }
198
220
199
- Profiler . EndSample ( ) ;
200
221
return rect ;
201
222
}
202
223
@@ -230,15 +251,38 @@ public void Blur()
230
251
RequiresRepaint = true ;
231
252
}
232
253
233
- private int ToggleNodeVisibility ( int idx , TreeNode rootNode )
254
+ private void ToggleNodeCheck ( int idx , TreeNode node )
255
+ {
256
+ if ( node . IsFolder )
257
+ {
258
+
259
+ }
260
+ else
261
+ {
262
+ switch ( node . CheckState )
263
+ {
264
+ case CheckState . Empty :
265
+ node . CheckState = CheckState . Checked ;
266
+ break ;
267
+
268
+ case CheckState . Checked :
269
+ node . CheckState = CheckState . Empty ;
270
+ break ;
271
+ }
272
+
273
+ Debug . LogFormat ( "Ripple CheckState index:{0} level:{1}" , idx , node . Level ) ;
274
+ }
275
+ }
276
+
277
+ private void ToggleNodeVisibility ( int idx , TreeNode node )
234
278
{
235
- var rootNodeLevel = rootNode . Level ;
236
- rootNode . IsCollapsed = ! rootNode . IsCollapsed ;
279
+ var nodeLevel = node . Level ;
280
+ node . IsCollapsed = ! node . IsCollapsed ;
237
281
idx ++ ;
238
- for ( ; idx < nodes . Count && nodes [ idx ] . Level > rootNodeLevel ; idx ++ )
282
+ for ( ; idx < nodes . Count && nodes [ idx ] . Level > nodeLevel ; idx ++ )
239
283
{
240
- nodes [ idx ] . IsHidden = rootNode . IsCollapsed ;
241
- if ( nodes [ idx ] . IsFolder && ! rootNode . IsCollapsed && nodes [ idx ] . IsCollapsed )
284
+ nodes [ idx ] . IsHidden = node . IsCollapsed ;
285
+ if ( nodes [ idx ] . IsFolder && ! node . IsCollapsed && nodes [ idx ] . IsCollapsed )
242
286
{
243
287
var level = nodes [ idx ] . Level ;
244
288
for ( idx ++ ; idx < nodes . Count && nodes [ idx ] . Level > level ; idx ++ ) { }
@@ -247,9 +291,8 @@ private int ToggleNodeVisibility(int idx, TreeNode rootNode)
247
291
}
248
292
if ( SelectedNode != null && SelectedNode . IsHidden )
249
293
{
250
- SelectedNode = rootNode ;
294
+ SelectedNode = node ;
251
295
}
252
- return idx ;
253
296
}
254
297
255
298
private bool HandleInput ( Rect rect , TreeNode currentNode , int index , Action < TreeNode > singleClick = null , Action < TreeNode > doubleClick = null , Action < TreeNode > rightClick = null )
@@ -406,55 +449,108 @@ public class TreeNode
406
449
public bool IsHidden ;
407
450
public bool IsActive ;
408
451
public GUIContent content ;
452
+ public bool Checkable ;
453
+ public CheckState CheckState ;
454
+
409
455
[ NonSerialized ] public Texture2D Icon ;
410
456
411
457
public void Load ( )
412
458
{
413
459
content = new GUIContent ( Label , Icon ) ;
414
460
}
415
461
416
- public bool Render ( Rect rect , float indentation , bool isSelected , GUIStyle folderStyle , GUIStyle nodeStyle , GUIStyle activeNodeStyle )
462
+ public TreeNodeRenderResult Render ( Rect rect , float indentation , bool isSelected , GUIStyle toggleStyle , GUIStyle nodeStyle , GUIStyle activeNodeStyle )
417
463
{
464
+ var renderResult = TreeNodeRenderResult . None ;
465
+
418
466
if ( IsHidden )
419
- return false ;
467
+ return renderResult ;
420
468
421
- GUIStyle style ;
422
- if ( IsFolder )
423
- {
424
- style = folderStyle ;
425
- }
426
- else
469
+ var fillRect = rect ;
470
+ var nodeStartX = Level * indentation * ( Checkable ? 2 : 1 ) ;
471
+
472
+ if ( Checkable && Level > 0 )
427
473
{
428
- style = IsActive ? activeNodeStyle : nodeStyle ;
474
+ nodeStartX += 2 * Level ;
429
475
}
430
476
431
- bool changed = false ;
432
- var fillRect = rect ;
433
- var nodeRect = new Rect ( Level * indentation , rect . y , rect . width , rect . height ) ;
477
+ var nodeRect = new Rect ( nodeStartX , rect . y , rect . width , rect . height ) ;
478
+
479
+ var data = string . Format ( "Label: {0} " , Label ) ;
480
+ data += string . Format ( "Start: {0} " , nodeStartX ) ;
434
481
435
482
if ( Event . current . type == EventType . repaint )
436
483
{
437
484
nodeStyle . Draw ( fillRect , GUIContent . none , false , false , false , isSelected ) ;
438
- if ( IsFolder )
485
+ }
486
+
487
+ var styleOn = false ;
488
+ if ( IsFolder )
489
+ {
490
+ data += string . Format ( "FolderStart: {0} " , nodeStartX ) ;
491
+
492
+ var toggleRect = new Rect ( nodeStartX , nodeRect . y , indentation , nodeRect . height ) ;
493
+ nodeStartX += toggleRect . width ;
494
+
495
+ styleOn = ! IsCollapsed ;
496
+
497
+ if ( Event . current . type == EventType . repaint )
498
+ {
499
+ toggleStyle . Draw ( toggleRect , GUIContent . none , false , false , styleOn , isSelected ) ;
500
+ }
501
+
502
+ EditorGUI . BeginChangeCheck ( ) ;
439
503
{
440
- style . Draw ( nodeRect , content , false , false , ! IsCollapsed , isSelected ) ;
504
+ GUI . Toggle ( toggleRect , ! IsCollapsed , GUIContent . none , GUIStyle . none ) ;
441
505
}
442
- else
506
+ if ( EditorGUI . EndChangeCheck ( ) )
443
507
{
444
- style . Draw ( nodeRect , content , false , false , false , isSelected ) ;
508
+ renderResult = TreeNodeRenderResult . VisibilityChange ;
445
509
}
446
510
}
447
511
448
- if ( IsFolder )
512
+ if ( Checkable )
449
513
{
450
- var toggleRect = new Rect ( nodeRect . x , nodeRect . y , style . border . horizontal , nodeRect . height ) ;
514
+ data += string . Format ( "SelectStart: {0} " , nodeStartX ) ;
515
+
516
+ var selectRect = new Rect ( nodeStartX , nodeRect . y , indentation , nodeRect . height ) ;
517
+
518
+ nodeStartX += selectRect . width + 2 ;
519
+
520
+ var selectionStyle = GUI . skin . toggle ;
521
+ var selectionValue = false ;
522
+
523
+ if ( CheckState == CheckState . Checked )
524
+ {
525
+ selectionValue = true ;
526
+ }
527
+ else if ( CheckState == CheckState . Mixed )
528
+ {
529
+ selectionStyle = Styles . ToggleMixedStyle ;
530
+ }
451
531
452
532
EditorGUI . BeginChangeCheck ( ) ;
453
- GUI . Toggle ( toggleRect , ! IsCollapsed , GUIContent . none , GUIStyle . none ) ;
454
- changed = EditorGUI . EndChangeCheck ( ) ;
533
+ {
534
+ GUI . Toggle ( selectRect , selectionValue , GUIContent . none , selectionStyle ) ;
535
+ }
536
+ if ( EditorGUI . EndChangeCheck ( ) )
537
+ {
538
+ renderResult = TreeNodeRenderResult . CheckChange ;
539
+ }
540
+ }
541
+
542
+ data += string . Format ( "ContentStart: {0} " , nodeStartX ) ;
543
+ var contentStyle = IsActive ? activeNodeStyle : nodeStyle ;
544
+
545
+ var contentRect = new Rect ( nodeStartX , rect . y , rect . width , rect . height ) ;
546
+ if ( Event . current . type == EventType . repaint )
547
+ {
548
+ contentStyle . Draw ( contentRect , content , false , false , styleOn , isSelected ) ;
455
549
}
456
550
457
- return changed ;
551
+ Debug . Log ( data ) ;
552
+
553
+ return renderResult ;
458
554
}
459
555
460
556
public override string ToString ( )
@@ -509,4 +605,18 @@ public void UpdateIcons(Texture2D activeBranchIcon, Texture2D branchIcon, Textur
509
605
}
510
606
}
511
607
}
608
+
609
+ public enum TreeNodeRenderResult
610
+ {
611
+ None ,
612
+ VisibilityChange ,
613
+ CheckChange
614
+ }
615
+
616
+ public enum CheckState
617
+ {
618
+ Empty ,
619
+ Checked ,
620
+ Mixed
621
+ }
512
622
}
0 commit comments