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

Commit 11686f5

Browse files
Merge pull request #511 from github-for-unity/enhancements/tree-view-checked-state
Toggling checked state in Tree view
2 parents 6d9e853 + 8dd8eb9 commit 11686f5

File tree

3 files changed

+150
-35
lines changed

3 files changed

+150
-35
lines changed

src/GitHub.Api/UI/TreeLoader.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ public interface ITree
88
{
99
void AddNode(string path, string label, int level, bool isFolder, bool isActive, bool isHidden, bool isCollapsed, bool isSelected);
1010
void Clear();
11-
HashSet<string> GetCollapsedFolders();
11+
IEnumerable<string> GetCollapsedFolders();
12+
IEnumerable<string> GetCheckedFiles();
1213
string Title { get; }
1314
bool DisplayRootNode { get; }
1415
bool IsCheckable { get; }
@@ -20,7 +21,7 @@ public static class TreeLoader
2021
{
2122
public static void Load(ITree tree, IEnumerable<ITreeData> treeDatas)
2223
{
23-
var collapsedFolders = tree.GetCollapsedFolders();
24+
var collapsedFolders = new HashSet<string>(tree.GetCollapsedFolders());
2425
var selectedNodePath = tree.SelectedNodePath;
2526

2627
tree.Clear();

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ public override void InitializeView(IView parent)
5959
targetMode = mode;
6060
}
6161

62-
6362
public override void OnEnable()
6463
{
6564
base.OnEnable();

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

Lines changed: 147 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
using System;
2-
using System.Collections;
32
using System.Collections.Generic;
43
using System.Linq;
54
using UnityEditor;
65
using UnityEngine;
7-
using UnityEngine.Profiling;
86

97
namespace 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

Comments
 (0)