Skip to content

Commit db3d784

Browse files
author
tznind
committed
Added caching of BuildLineMap results for better performance with very large trees
1 parent 14244d2 commit db3d784

1 file changed

Lines changed: 48 additions & 18 deletions

File tree

‎Terminal.Gui/Views/TreeView.cs‎

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,13 @@ public T SelectedObject {
319319
/// <summary>
320320
/// Secondary selected regions of tree when <see cref="MultiSelect"/> is true
321321
/// </summary>
322-
private Stack<TreeSelection<T>> _multiSelectedRegions = new Stack<TreeSelection<T>>();
322+
private Stack<TreeSelection<T>> multiSelectedRegions = new Stack<TreeSelection<T>>();
323323

324+
/// <summary>
325+
/// Cached result of <see cref="BuildLineMap"/>
326+
/// </summary>
327+
private Branch<T>[] cachedLineMap;
328+
324329

325330
/// <summary>
326331
/// Error message to display when the control is not properly initialized at draw time (nodes added but no tree builder set)
@@ -400,18 +405,21 @@ public void AddObject(T o)
400405
{
401406
if(!roots.ContainsKey(o)) {
402407
roots.Add(o,new Branch<T>(this,null,o));
408+
InvalidateLineMap();
403409
SetNeedsDisplay();
404410
}
405411
}
406412

413+
407414
/// <summary>
408415
/// Removes all objects from the tree and clears <see cref="SelectedObject"/>
409416
/// </summary>
410417
public void ClearObjects()
411418
{
412419
SelectedObject = default(T);
413-
_multiSelectedRegions.Clear();
420+
multiSelectedRegions.Clear();
414421
roots = new Dictionary<T, Branch<T>>();
422+
InvalidateLineMap();
415423
SetNeedsDisplay();
416424
}
417425

@@ -424,6 +432,7 @@ public void Remove(T o)
424432
{
425433
if(roots.ContainsKey(o)) {
426434
roots.Remove(o);
435+
InvalidateLineMap();
427436
SetNeedsDisplay();
428437

429438
if(Equals(SelectedObject,o))
@@ -445,9 +454,11 @@ public void AddObjects(IEnumerable<T> collection)
445454
objectsAdded = true;
446455
}
447456
}
448-
449-
if(objectsAdded)
457+
458+
if (objectsAdded) {
459+
InvalidateLineMap();
450460
SetNeedsDisplay();
461+
}
451462
}
452463

453464
/// <summary>
@@ -461,6 +472,7 @@ public void RefreshObject (T o, bool startAtTop = false)
461472
var branch = ObjectToBranch(o);
462473
if(branch != null) {
463474
branch.Refresh(startAtTop);
475+
InvalidateLineMap();
464476
SetNeedsDisplay();
465477
}
466478

@@ -474,6 +486,7 @@ public void RebuildTree()
474486
foreach(var branch in roots.Values)
475487
branch.Rebuild();
476488

489+
InvalidateLineMap();
477490
SetNeedsDisplay();
478491
}
479492

@@ -590,13 +603,16 @@ public int GetContentWidth(bool visible){
590603
/// <returns></returns>
591604
private Branch<T>[] BuildLineMap()
592605
{
606+
if(cachedLineMap != null)
607+
return cachedLineMap;
608+
593609
List<Branch<T>> toReturn = new List<Branch<T>>();
594610

595611
foreach(var root in roots.Values) {
596612
toReturn.AddRange(AddToLineMap(root));
597613
}
598614

599-
return toReturn.ToArray();
615+
return cachedLineMap = toReturn.ToArray();
600616
}
601617

602618
private IEnumerable<Branch<T>> AddToLineMap (Branch<T> currentBranch)
@@ -733,14 +749,14 @@ public override bool MouseEvent (MouseEvent me)
733749
clickedBranch.Expand();
734750
else {
735751
SelectedObject = clickedBranch.Model; // It is a leaf node
736-
_multiSelectedRegions.Clear();
752+
multiSelectedRegions.Clear();
737753
}
738754
}
739755
else {
740756

741757
// It is a first click somewhere in the current line that doesn't look like an expansion/collapse attempt
742758
SelectedObject = clickedBranch.Model;
743-
_multiSelectedRegions.Clear();
759+
multiSelectedRegions.Clear();
744760
}
745761

746762
SetNeedsDisplay();
@@ -829,7 +845,7 @@ public void AdjustSelection (int offset, bool expandSelection = false)
829845
{
830846
// if it is not a shift click or we don't allow multi select
831847
if(!expandSelection || !MultiSelect)
832-
_multiSelectedRegions.Clear();
848+
multiSelectedRegions.Clear();
833849

834850
if(SelectedObject == null){
835851
SelectedObject = roots.Keys.FirstOrDefault();
@@ -852,16 +868,16 @@ public void AdjustSelection (int offset, bool expandSelection = false)
852868
// If it is a multi selection
853869
if(expandSelection && MultiSelect)
854870
{
855-
if(_multiSelectedRegions.Any())
871+
if(multiSelectedRegions.Any())
856872
{
857873
// expand the existing head selection
858-
var head = _multiSelectedRegions.Pop();
859-
_multiSelectedRegions.Push(new TreeSelection<T>(head.Origin,newIdx,map));
874+
var head = multiSelectedRegions.Pop();
875+
multiSelectedRegions.Push(new TreeSelection<T>(head.Origin,newIdx,map));
860876
}
861877
else
862878
{
863879
// or start a new multi selection region
864-
_multiSelectedRegions.Push(new TreeSelection<T>(map[idx],newIdx,map));
880+
multiSelectedRegions.Push(new TreeSelection<T>(map[idx],newIdx,map));
865881
}
866882
}
867883

@@ -882,7 +898,8 @@ public void AdjustSelection (int offset, bool expandSelection = false)
882898
}
883899

884900
}
885-
901+
902+
InvalidateLineMap();
886903
SetNeedsDisplay();
887904
}
888905

@@ -896,6 +913,7 @@ public void Expand(T toExpand)
896913
return;
897914

898915
ObjectToBranch(toExpand)?.Expand();
916+
InvalidateLineMap();
899917
SetNeedsDisplay();
900918
}
901919

@@ -909,6 +927,7 @@ public void ExpandAll(T toExpand)
909927
return;
910928

911929
ObjectToBranch(toExpand)?.ExpandAll();
930+
InvalidateLineMap();
912931
SetNeedsDisplay();
913932
}
914933
/// <summary>
@@ -919,7 +938,8 @@ public void ExpandAll()
919938
foreach (var item in roots) {
920939
item.Value.ExpandAll();
921940
}
922-
941+
942+
InvalidateLineMap();
923943
SetNeedsDisplay();
924944
}
925945
/// <summary>
@@ -968,7 +988,8 @@ public void CollapseAll()
968988
foreach (var item in roots) {
969989
item.Value.Collapse();
970990
}
971-
991+
992+
InvalidateLineMap();
972993
SetNeedsDisplay();
973994
}
974995

@@ -1001,8 +1022,17 @@ protected void CollapseImpl(T toCollapse, bool all)
10011022
SelectedObject = null;
10021023
}
10031024

1025+
InvalidateLineMap();
10041026
SetNeedsDisplay();
10051027
}
1028+
1029+
/// <summary>
1030+
/// Clears any cached results of <see cref="BuildLineMap"/>
1031+
/// </summary>
1032+
protected void InvalidateLineMap()
1033+
{
1034+
cachedLineMap = null;
1035+
}
10061036

10071037
/// <summary>
10081038
/// Returns the corresponding <see cref="Branch{T}"/> in the tree for <paramref name="toFind"/>. This will not work for objects hidden by their parent being collapsed
@@ -1022,7 +1052,7 @@ private Branch<T> ObjectToBranch(T toFind)
10221052
public bool IsSelected (T model)
10231053
{
10241054
return Equals(SelectedObject , model) ||
1025-
(MultiSelect && _multiSelectedRegions.Any(s=>s.Contains(model)));
1055+
(MultiSelect && multiSelectedRegions.Any(s=>s.Contains(model)));
10261056
}
10271057

10281058
/// <summary>
@@ -1054,14 +1084,14 @@ public void SelectAll()
10541084
if(!MultiSelect)
10551085
return;
10561086

1057-
_multiSelectedRegions.Clear();
1087+
multiSelectedRegions.Clear();
10581088

10591089
var map = BuildLineMap();
10601090

10611091
if(map.Length == 0)
10621092
return;
10631093

1064-
_multiSelectedRegions.Push(new TreeSelection<T>(map[0],map.Length,map));
1094+
multiSelectedRegions.Push(new TreeSelection<T>(map[0],map.Length,map));
10651095
SetNeedsDisplay();
10661096

10671097
OnSelectionChanged(new SelectionChangedEventArgs<T>(this,SelectedObject,SelectedObject));

0 commit comments

Comments
 (0)