Skip to content
This repository was archived by the owner on Dec 5, 2024. It is now read-only.

Commit e36d932

Browse files
Merge branch 'enhancements/tree-improvements' into enhancements/changes-tree-view
2 parents 0de3651 + 656007e commit e36d932

File tree

1 file changed

+172
-62
lines changed

1 file changed

+172
-62
lines changed

src/UnityExtension/Assets/Editor/GitHub.Unity/UI/TreeControl.cs

Lines changed: 172 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ public abstract class Tree
2020
[SerializeField] public Rect Margin = new Rect();
2121
[SerializeField] public Rect Padding = new Rect();
2222

23+
[SerializeField] public string PathIgnoreRoot;
24+
[SerializeField] public string PathSeparator = "/";
25+
[SerializeField] public bool DisplayRootNode = true;
26+
[SerializeField] public bool Checkable = false;
2327
[SerializeField] public GUIStyle FolderStyle;
2428
[SerializeField] public GUIStyle TreeNodeStyle;
2529
[SerializeField] public GUIStyle ActiveTreeNodeStyle;
@@ -58,12 +62,15 @@ public void Load(IEnumerable<ITreeData> data, string title)
5862
folders.Clear();
5963
nodes.Clear();
6064

65+
var displayRootLevel = DisplayRootNode ? 1 : 0;
66+
6167
var titleNode = new TreeNode()
6268
{
6369
Name = title,
6470
Label = title,
65-
Level = 0,
66-
IsFolder = true
71+
Level = -1 + displayRootLevel,
72+
IsFolder = true,
73+
Checkable = Checkable
6774
};
6875
SetNodeIcon(titleNode);
6976
nodes.Add(titleNode);
@@ -73,12 +80,22 @@ public void Load(IEnumerable<ITreeData> data, string title)
7380

7481
foreach (var d in data)
7582
{
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);
7794
for (int i = 0; i < parts.Length; i++)
7895
{
7996
var label = parts[i];
8097
var level = i + 1;
81-
var name = String.Join("/", parts, 0, level);
98+
var name = String.Join(PathSeparator, parts, 0, level);
8299
var isFolder = i < parts.Length - 1;
83100
var alreadyExists = folders.ContainsKey(name);
84101
if (!alreadyExists)
@@ -88,8 +105,9 @@ public void Load(IEnumerable<ITreeData> data, string title)
88105
Name = name,
89106
IsActive = d.IsActive,
90107
Label = label,
91-
Level = level,
92-
IsFolder = isFolder
108+
Level = i + displayRootLevel,
109+
IsFolder = isFolder,
110+
Checkable = Checkable
93111
};
94112

95113
if (node.IsActive)
@@ -134,27 +152,32 @@ public void Load(IEnumerable<ITreeData> data, string title)
134152

135153
public Rect Render(Rect rect, Vector2 scroll, Action<TreeNode> singleClick = null, Action<TreeNode> doubleClick = null, Action<TreeNode> rightClick = null)
136154
{
137-
Profiler.BeginSample("TreeControl");
138-
bool visible = true;
139-
var availableHeight = rect.y + rect.height;
140-
141155
RequiresRepaint = false;
142156
rect = new Rect(0f, rect.y, rect.width, ItemHeight);
143157

144-
var titleNode = nodes[0];
145-
bool selectionChanged = titleNode.Render(rect, 0f, selectedNode == titleNode, FolderStyle, TreeNodeStyle, ActiveTreeNodeStyle);
158+
var level = 0;
146159

147-
if (selectionChanged)
160+
if (DisplayRootNode)
148161
{
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+
}
151173

152-
RequiresRepaint = HandleInput(rect, titleNode, 0);
153-
rect.y += ItemHeight + ItemSpacing;
174+
RequiresRepaint = HandleInput(rect, titleNode, 0);
175+
rect.y += ItemHeight + ItemSpacing;
154176

155-
Indent();
177+
Indent();
178+
level = 1;
179+
}
156180

157-
int level = 1;
158181
int i = 1;
159182
for (; i < nodes.Count; i++)
160183
{
@@ -163,16 +186,15 @@ public Rect Render(Rect rect, Vector2 scroll, Action<TreeNode> singleClick = nul
163186
{
164187
Indent();
165188
}
189+
var renderResult = node.Render(rect, Styles.TreeIndentation, selectedNode == node, FolderStyle, TreeNodeStyle, ActiveTreeNodeStyle);
166190

167-
if (visible)
191+
if (renderResult == TreeNodeRenderResult.VisibilityChange)
168192
{
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);
176198
}
177199

178200
if (node.Level < level)
@@ -186,17 +208,16 @@ public Rect Render(Rect rect, Vector2 scroll, Action<TreeNode> singleClick = nul
186208

187209
if (!node.IsHidden)
188210
{
189-
if (visible)
190-
{
191-
RequiresRepaint = HandleInput(rect, node, i, singleClick, doubleClick, rightClick);
192-
}
211+
RequiresRepaint = HandleInput(rect, node, i, singleClick, doubleClick, rightClick);
193212
rect.y += ItemHeight + ItemSpacing;
194213
}
195214
}
196215

197-
Unindent();
216+
if (DisplayRootNode)
217+
{
218+
Unindent();
219+
}
198220

199-
Profiler.EndSample();
200221
return rect;
201222
}
202223

@@ -230,15 +251,38 @@ public void Blur()
230251
RequiresRepaint = true;
231252
}
232253

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)
234278
{
235-
var rootNodeLevel = rootNode.Level;
236-
rootNode.IsCollapsed = !rootNode.IsCollapsed;
279+
var nodeLevel = node.Level;
280+
node.IsCollapsed = !node.IsCollapsed;
237281
idx++;
238-
for (; idx < nodes.Count && nodes[idx].Level > rootNodeLevel; idx++)
282+
for (; idx < nodes.Count && nodes[idx].Level > nodeLevel; idx++)
239283
{
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)
242286
{
243287
var level = nodes[idx].Level;
244288
for (idx++; idx < nodes.Count && nodes[idx].Level > level; idx++) { }
@@ -247,9 +291,8 @@ private int ToggleNodeVisibility(int idx, TreeNode rootNode)
247291
}
248292
if (SelectedNode != null && SelectedNode.IsHidden)
249293
{
250-
SelectedNode = rootNode;
294+
SelectedNode = node;
251295
}
252-
return idx;
253296
}
254297

255298
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
406449
public bool IsHidden;
407450
public bool IsActive;
408451
public GUIContent content;
452+
public bool Checkable;
453+
public CheckState CheckState;
454+
409455
[NonSerialized] public Texture2D Icon;
410456

411457
public void Load()
412458
{
413459
content = new GUIContent(Label, Icon);
414460
}
415461

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)
417463
{
464+
var renderResult = TreeNodeRenderResult.None;
465+
418466
if (IsHidden)
419-
return false;
467+
return renderResult;
420468

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)
427473
{
428-
style = IsActive ? activeNodeStyle : nodeStyle;
474+
nodeStartX += 2 * Level;
429475
}
430476

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);
434481

435482
if (Event.current.type == EventType.repaint)
436483
{
437484
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();
439503
{
440-
style.Draw(nodeRect, content, false, false, !IsCollapsed, isSelected);
504+
GUI.Toggle(toggleRect, !IsCollapsed, GUIContent.none, GUIStyle.none);
441505
}
442-
else
506+
if (EditorGUI.EndChangeCheck())
443507
{
444-
style.Draw(nodeRect, content, false, false, false, isSelected);
508+
renderResult = TreeNodeRenderResult.VisibilityChange;
445509
}
446510
}
447511

448-
if (IsFolder)
512+
if (Checkable)
449513
{
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+
}
451531

452532
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);
455549
}
456550

457-
return changed;
551+
Debug.Log(data);
552+
553+
return renderResult;
458554
}
459555

460556
public override string ToString()
@@ -509,4 +605,18 @@ public void UpdateIcons(Texture2D activeBranchIcon, Texture2D branchIcon, Textur
509605
}
510606
}
511607
}
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+
}
512622
}

0 commit comments

Comments
 (0)