瀏覽代碼

Added caching of BuildLineMap results for better performance with very large trees

tznind 4 年之前
父節點
當前提交
db3d784a1a
共有 1 個文件被更改,包括 48 次插入18 次删除
  1. 48 18
      Terminal.Gui/Views/TreeView.cs

+ 48 - 18
Terminal.Gui/Views/TreeView.cs

@@ -319,8 +319,13 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Secondary selected regions of tree when <see cref="MultiSelect"/> is true
 		/// </summary>
-		private Stack<TreeSelection<T>> _multiSelectedRegions = new Stack<TreeSelection<T>>();
+		private Stack<TreeSelection<T>> multiSelectedRegions = new Stack<TreeSelection<T>>();
 		
+		/// <summary>
+		/// Cached result of <see cref="BuildLineMap"/>
+		/// </summary>
+		private Branch<T>[] cachedLineMap;
+
 
 		/// <summary>
 		/// 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 @@ namespace Terminal.Gui {
 		{
 			if(!roots.ContainsKey(o)) {
 				roots.Add(o,new Branch<T>(this,null,o));
+				InvalidateLineMap();
 				SetNeedsDisplay();
 			}
 		}
 
+
 		/// <summary>
 		/// Removes all objects from the tree and clears <see cref="SelectedObject"/>
 		/// </summary>
 		public void ClearObjects()
 		{
 			SelectedObject = default(T);
-			_multiSelectedRegions.Clear();
+			multiSelectedRegions.Clear();
 			roots = new Dictionary<T, Branch<T>>();
+			InvalidateLineMap();
 			SetNeedsDisplay();
 		}
 
@@ -424,6 +432,7 @@ namespace Terminal.Gui {
 		{
 			if(roots.ContainsKey(o)) {
 				roots.Remove(o);
+				InvalidateLineMap();
 				SetNeedsDisplay();
 
 				if(Equals(SelectedObject,o))
@@ -445,9 +454,11 @@ namespace Terminal.Gui {
 					objectsAdded = true;
 				}	
 			}
-				
-			if(objectsAdded)
+
+			if (objectsAdded) {
+				InvalidateLineMap();
 				SetNeedsDisplay();
+			}
 		}
 
 		/// <summary>
@@ -461,6 +472,7 @@ namespace Terminal.Gui {
 			var branch = ObjectToBranch(o);
 			if(branch != null) {
 				branch.Refresh(startAtTop);
+				InvalidateLineMap();
 				SetNeedsDisplay();
 			}
 
@@ -474,6 +486,7 @@ namespace Terminal.Gui {
 			foreach(var branch in roots.Values)
 				branch.Rebuild();
 			
+			InvalidateLineMap();
 			SetNeedsDisplay();
 		}
 
@@ -590,13 +603,16 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		private Branch<T>[] BuildLineMap()
 		{
+			if(cachedLineMap != null)
+				return cachedLineMap;
+
 			List<Branch<T>> toReturn = new List<Branch<T>>();
 
 			foreach(var root in roots.Values) {
 				toReturn.AddRange(AddToLineMap(root));
 			}
 
-			return toReturn.ToArray();
+			return cachedLineMap = toReturn.ToArray();
 		}
 
 		private IEnumerable<Branch<T>> AddToLineMap (Branch<T> currentBranch)
@@ -733,14 +749,14 @@ namespace Terminal.Gui {
 						clickedBranch.Expand();
 					else {
 						SelectedObject = clickedBranch.Model; // It is a leaf node
-						_multiSelectedRegions.Clear();
+						multiSelectedRegions.Clear();
 					}
 				}
 				else {
 
 					// It is a first click somewhere in the current line that doesn't look like an expansion/collapse attempt
 					SelectedObject = clickedBranch.Model;
-					_multiSelectedRegions.Clear();
+					multiSelectedRegions.Clear();
 				}
 
 				SetNeedsDisplay();
@@ -829,7 +845,7 @@ namespace Terminal.Gui {
 		{
 			// if it is not a shift click or we don't allow multi select
 			if(!expandSelection || !MultiSelect)
-				_multiSelectedRegions.Clear();
+				multiSelectedRegions.Clear();
 
 			if(SelectedObject == null){
 				SelectedObject = roots.Keys.FirstOrDefault();
@@ -852,16 +868,16 @@ namespace Terminal.Gui {
 					// If it is a multi selection
 					if(expandSelection && MultiSelect)
 					{
-						if(_multiSelectedRegions.Any())
+						if(multiSelectedRegions.Any())
 						{
 							// expand the existing head selection
-							var head = _multiSelectedRegions.Pop();
-							_multiSelectedRegions.Push(new TreeSelection<T>(head.Origin,newIdx,map));
+							var head = multiSelectedRegions.Pop();
+							multiSelectedRegions.Push(new TreeSelection<T>(head.Origin,newIdx,map));
 						}
 						else
 						{
 							// or start a new multi selection region
-							_multiSelectedRegions.Push(new TreeSelection<T>(map[idx],newIdx,map));
+							multiSelectedRegions.Push(new TreeSelection<T>(map[idx],newIdx,map));
 						}
 					}
 
@@ -882,7 +898,8 @@ namespace Terminal.Gui {
 				}
 
 			}
-						
+
+			InvalidateLineMap();						
 			SetNeedsDisplay();
 		}
 
@@ -896,6 +913,7 @@ namespace Terminal.Gui {
 				return;
 			
 			ObjectToBranch(toExpand)?.Expand();
+			InvalidateLineMap();
 			SetNeedsDisplay();
 		}
 		
@@ -909,6 +927,7 @@ namespace Terminal.Gui {
 				return;
 			
 			ObjectToBranch(toExpand)?.ExpandAll();
+			InvalidateLineMap();
 			SetNeedsDisplay();
 		}
 		/// <summary>
@@ -919,7 +938,8 @@ namespace Terminal.Gui {
 			foreach (var item in roots) {
 				item.Value.ExpandAll();
 			}
-
+			
+			InvalidateLineMap();
 			SetNeedsDisplay();
 		}
 		/// <summary>
@@ -968,7 +988,8 @@ namespace Terminal.Gui {
 			foreach (var item in roots) {
 				item.Value.Collapse();
 			}
-
+			
+			InvalidateLineMap();
 			SetNeedsDisplay();
 		}
 
@@ -1001,8 +1022,17 @@ namespace Terminal.Gui {
 				SelectedObject = null;
 			}	
 			
+			InvalidateLineMap();
 			SetNeedsDisplay();
 		}
+		
+		/// <summary>
+		/// Clears any cached results of <see cref="BuildLineMap"/>
+		/// </summary>
+		protected void InvalidateLineMap()
+		{
+			cachedLineMap = null;
+		}
 
 		/// <summary>
 		/// 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 @@ namespace Terminal.Gui {
 		public bool IsSelected (T model)
 		{
 			return Equals(SelectedObject , model) ||
-				(MultiSelect && _multiSelectedRegions.Any(s=>s.Contains(model)));
+				(MultiSelect && multiSelectedRegions.Any(s=>s.Contains(model)));
 		}
 
 		/// <summary>
@@ -1054,14 +1084,14 @@ namespace Terminal.Gui {
 			if(!MultiSelect)
 				return;
 
-			_multiSelectedRegions.Clear();
+			multiSelectedRegions.Clear();
 
 			var map = BuildLineMap();
 
 			if(map.Length == 0)
 				return;
 
-			_multiSelectedRegions.Push(new TreeSelection<T>(map[0],map.Length,map));
+			multiSelectedRegions.Push(new TreeSelection<T>(map[0],map.Length,map));
 			SetNeedsDisplay();
 			
 			OnSelectionChanged(new SelectionChangedEventArgs<T>(this,SelectedObject,SelectedObject));