Browse Source

Added method RebuildTree
Similar to RefreshObject but for all visible branches. Call this method when making large scale changes behind the scenes to objects including hierarchy changes.

tznind 4 years ago
parent
commit
7af2763f43
2 changed files with 126 additions and 1 deletions
  1. 35 1
      Terminal.Gui/Views/TreeView.cs
  2. 91 0
      UnitTests/TreeViewTests.cs

+ 35 - 1
Terminal.Gui/Views/TreeView.cs

@@ -76,7 +76,17 @@ namespace Terminal.Gui {
 			}
 			}
 
 
 		}
 		}
-
+		
+		/// <summary>
+		/// Rebuilds the tree structure for all exposed objects starting with the root objects.  Call this method when you know there are changes to the tree but don't know which objects have changed (otherwise use <see cref="RefreshObject(object, bool)"/>)
+		/// </summary>
+		public void RebuildTree()
+		{
+			foreach(var branch in roots.Values)
+				branch.Rebuild();
+			
+			SetNeedsDisplay();
+		}
 
 
 		/// <summary>
 		/// <summary>
 		/// The root objects in the tree, note that this collection is of root objects only
 		/// The root objects in the tree, note that this collection is of root objects only
@@ -609,6 +619,30 @@ namespace Terminal.Gui {
 			}
 			}
 			
 			
 		}
 		}
+
+		/// <summary>
+		/// Calls <see cref="Refresh(bool)"/> on the current branch and all expanded children
+		/// </summary>
+		internal void Rebuild()
+		{
+			Refresh(false);
+
+			// if we know about our children
+			if(ChildBranches != null) {
+				if(IsExpanded) {
+					//if we are expanded we need to updatethe visible children
+					foreach(var child in ChildBranches) {
+						child.Value.Refresh(false);
+					}
+					
+				}
+				else {
+					// we are not expanded so should forget about children because they may not exist anymore
+					ChildBranches = null;
+				}
+			}
+				
+		}
 	}
 	}
    
    
 	/// <summary>
 	/// <summary>

+ 91 - 0
UnitTests/TreeViewTests.cs

@@ -167,6 +167,12 @@ namespace UnitTests {
 			// The old selection was c1 which is now gone so selection should default to the parent of that branch (the factory)
 			// The old selection was c1 which is now gone so selection should default to the parent of that branch (the factory)
 			Assert.Equal(f,tree.SelectedObject);
 			Assert.Equal(f,tree.SelectedObject);
 		}
 		}
+
+		/// <summary>
+		/// Tests that <see cref="TreeView.GetParent(object)"/> returns the parent object for
+		/// Cars (Factories).  Note that the method only works once the parent branch (Factory)
+		/// is expanded to expose the child (Car)
+		/// </summary>
 		[Fact]
 		[Fact]
 		public void GetParent_ReturnsParentOnlyWhenExpanded()
 		public void GetParent_ReturnsParentOnlyWhenExpanded()
 		{
 		{
@@ -190,6 +196,91 @@ namespace UnitTests {
 			Assert.Null(tree.GetParent(c2));
 			Assert.Null(tree.GetParent(c2));
 		}
 		}
 
 
+		/// <summary>
+		/// Tests how the tree adapts to changes in the ChildrenGetter delegate during runtime
+		/// when some branches are expanded and the new delegate returns children for a node that
+		/// previously didn't have any children
+		/// </summary>
+		[Fact]
+		public void RefreshObject_AfterChangingChildrenGetterDuringRuntime()
+		{
+			var tree = CreateTree(out Factory f, out Car c1, out Car c2);
+			
+			string wheel = "Shiny Wheel";
+
+			// Expand the Factory
+			tree.Expand(f);
+			
+			// c1 cannot have children
+			Assert.Equal(f,tree.GetParent(c1));
+
+			// expanding it does nothing
+			tree.Expand(c1);
+			Assert.False(tree.IsExpanded(c1));
+
+			// change the children getter so that now cars can have wheels
+			tree.ChildrenGetter = (o)=>
+				// factories have cars
+				o is Factory ? new object[]{c1,c2} 
+				// cars have wheels
+				: new object[]{wheel };
+			
+			// still cannot expand
+			tree.Expand(c1);
+			Assert.False(tree.IsExpanded(c1));
+
+			tree.RefreshObject(c1);
+			tree.Expand(c1);
+			Assert.True(tree.IsExpanded(c1));
+			Assert.Equal(wheel,tree.GetChildren(c1).FirstOrDefault());
+		}
+		/// <summary>
+		/// Same as <see cref="RefreshObject_AfterChangingChildrenGetterDuringRuntime"/> but
+		/// uses <see cref="TreeView.RebuildTree()"/> instead of <see cref="TreeView.RefreshObject(object, bool)"/>
+		/// </summary>
+		[Fact]
+		public void RebuildTree_AfterChangingChildrenGetterDuringRuntime()
+		{
+			var tree = CreateTree(out Factory f, out Car c1, out Car c2);
+			
+			string wheel = "Shiny Wheel";
+
+			// Expand the Factory
+			tree.Expand(f);
+			
+			// c1 cannot have children
+			Assert.Equal(f,tree.GetParent(c1));
+
+			// expanding it does nothing
+			tree.Expand(c1);
+			Assert.False(tree.IsExpanded(c1));
+
+			// change the children getter so that now cars can have wheels
+			tree.ChildrenGetter = (o)=>
+				// factories have cars
+				o is Factory ? new object[]{c1,c2} 
+				// cars have wheels
+				: new object[]{wheel };
+			
+			// still cannot expand
+			tree.Expand(c1);
+			Assert.False(tree.IsExpanded(c1));
+
+			// Rebuild the tree
+			tree.RebuildTree();
+			
+			// Rebuild should not have collapsed any branches or done anything wierd
+			Assert.True(tree.IsExpanded(f));
+
+			tree.Expand(c1);
+			Assert.True(tree.IsExpanded(c1));
+			Assert.Equal(wheel,tree.GetChildren(c1).FirstOrDefault());
+		}
+		/// <summary>
+		/// Tests that <see cref="TreeView.GetChildren(object)"/> returns the child objects for
+		/// the factory.  Note that the method only works once the parent branch (Factory)
+		/// is expanded to expose the child (Car)
+		/// </summary>
 		[Fact]
 		[Fact]
 		public void GetChildren_ReturnsChildrenOnlyWhenExpanded()
 		public void GetChildren_ReturnsChildrenOnlyWhenExpanded()
 		{
 		{