diff --git a/Terminal.Gui/Views/TreeView.cs b/Terminal.Gui/Views/TreeView.cs
index 31a03f178..8d95e067e 100644
--- a/Terminal.Gui/Views/TreeView.cs
+++ b/Terminal.Gui/Views/TreeView.cs
@@ -64,30 +64,146 @@ namespace Terminal.Gui {
{
Text = text;
}
-
-
}
///
- /// Hierarchical tree view with expandable branches. Branch objects are dynamically determined when expanded using a user defined
+ /// Interface for supplying data to a on demand as root level nodes are expanded by the user
+ ///
+ public interface ITreeBuilder
+ {
+ ///
+ /// Returns true if is implemented by this class
+ ///
+ ///
+ bool SupportsCanExpand {get;}
+
+ ///
+ /// Returns true/false for whether a model has children. This method should be implemented when is an expensive operation otherwise should return false (in which case this method will not be called)
+ ///
+ ///
+ ///
+ bool CanExpand(object model);
+
+ ///
+ /// Returns all children of a given which should be added to the tree as new branches underneath it
+ ///
+ ///
+ ///
+ IEnumerable GetChildren(object model);
+ }
+
+ ///
+ /// Abstract implementation of
+ ///
+ public abstract class TreeBuilder : ITreeBuilder {
+
+ ///
+ public bool SupportsCanExpand { get; protected set;} = false;
+
+ ///
+ /// Override this method to return a rapid answer as to whether returns results.
+ ///
+ ///
+ ///
+ public virtual bool CanExpand (object model){
+
+ return GetChildren(model).Any();
+ }
+
+ ///
+ public abstract IEnumerable GetChildren (object model);
+
+ ///
+ /// Constructs base and initializes
+ ///
+ /// Pass true if you intend to implement otherwise false
+ public TreeBuilder(bool supportsCanExpand)
+ {
+ SupportsCanExpand = supportsCanExpand;
+ }
+ }
+
+ ///
+ /// implementation for objects
+ ///
+ public class TreeNodeBuilder : TreeBuilder {
+
+ ///
+ /// Initialises a new instance of builder for any model objects of Type
+ ///
+ public TreeNodeBuilder():base(false)
+ {
+
+ }
+
+ ///
+ /// Returns from
+ ///
+ ///
+ ///
+ public override IEnumerable GetChildren (object model)
+ {
+ return model is ITreeNode n ? n.Children : Enumerable.Empty();
+ }
+ }
+
+ ///
+ /// Implementation of that uses user defined functions
+ ///
+ public class DelegateTreeBuilder : TreeBuilder
+ {
+ private Func> childGetter;
+ private Func canExpand;
+
+ ///
+ /// Constructs an implementation of that calls the user defined method to determine children
+ ///
+ ///
+ ///
+ public DelegateTreeBuilder(Func> childGetter) : base(false)
+ {
+ this.childGetter = childGetter;
+ }
+
+ ///
+ /// Constructs an implementation of that calls the user defined method to determine children and to determine expandability
+ ///
+ ///
+ ///
+ ///
+ public DelegateTreeBuilder(Func> childGetter, Func canExpand) : base(true)
+ {
+ this.childGetter = childGetter;
+ this.canExpand = canExpand;
+ }
+
+ ///
+ /// Returns whether a node can be expanded based on the delegate passed during construction
+ ///
+ ///
+ ///
+ public override bool CanExpand (object model)
+ {
+ return canExpand?.Invoke(model) ?? base.CanExpand (model);
+ }
+
+ ///
+ /// Returns children using the delegate method passed during construction
+ ///
+ ///
+ ///
+ public override IEnumerable GetChildren (object model)
+ {
+ return childGetter.Invoke(model);
+ }
+ }
+
+
+ ///
+ /// Hierarchical tree view with expandable branches. Branch objects are dynamically determined when expanded using a user defined
///
public class TreeView : View
{
- ///
- /// Default implementation of a . Supports returning children of or otherwise returns an empty collection (i.e. no children)
- ///
- static ChildrenGetterDelegate DefaultChildrenGetter = (s)=>{return s is ITreeNode n ? n.Children : Enumerable.Empty();};
-
- ///
- /// This is the delegate that will be used to fetch the children of a model object
- ///
- public ChildrenGetterDelegate ChildrenGetter {
- get { return childrenGetter ?? DefaultChildrenGetter; }
- set { childrenGetter = value; }
- }
-
- private ChildrenGetterDelegate childrenGetter;
- private CanExpandGetterDelegate canExpandGetter;
private int scrollOffset;
///
@@ -97,13 +213,10 @@ namespace Terminal.Gui {
public bool ShowBranchLines {get;set;} = true;
///
- /// Optional delegate where is expensive. This should quickly return true/false for whether an object is expandable. (e.g. indicating to a user that all folders can be expanded because they are folders without having to calculate contents)
+ /// Determines how sub branches of the tree are dynamically built at runtime as the user expands root nodes
///
- /// When this is null is used directly to determine if a node should be expandable
- public CanExpandGetterDelegate CanExpandGetter {
- get { return canExpandGetter; }
- set { canExpandGetter = value; }
- }
+ ///
+ public ITreeBuilder TreeBuilder { get;set;}
///
/// private variable for
@@ -179,11 +292,20 @@ namespace Terminal.Gui {
///
- /// Creates a new tree view with absolute positioning. Use to set set root objects for the tree
+ /// Creates a new tree view with absolute positioning. Use to set set root objects for the tree.
///
- public TreeView ():base()
+ public TreeView():base()
{
CanFocus = true;
+ TreeBuilder = new TreeNodeBuilder();
+ }
+
+ ///
+ /// Initialises .Creates a new tree view with absolute positioning. Use to set set root objects for the tree.
+ ///
+ public TreeView(ITreeBuilder builder) : this()
+ {
+ TreeBuilder = builder;
}
///
@@ -580,10 +702,10 @@ namespace Terminal.Gui {
///
public virtual void FetchChildren()
{
- if (tree.ChildrenGetter == null)
+ if (tree.TreeBuilder == null)
return;
- var children = tree.ChildrenGetter(this.Model) ?? new object[0];
+ var children = tree.TreeBuilder.GetChildren(this.Model) ?? Enumerable.Empty();
this.ChildBranches = children.ToDictionary(k=>k,val=>new Branch(tree,this,val));
}
@@ -684,8 +806,8 @@ namespace Terminal.Gui {
if(ChildBranches == null) {
//if there is a rapid method for determining whether there are children
- if(tree.CanExpandGetter != null) {
- return tree.CanExpandGetter(Model) ? tree.ExpandableSymbol : leafSymbol;
+ if(tree.TreeBuilder.SupportsCanExpand) {
+ return tree.TreeBuilder.CanExpand(Model) ? tree.ExpandableSymbol : leafSymbol;
}
//there is no way of knowing whether we can expand without fetching the children
@@ -736,7 +858,7 @@ namespace Terminal.Gui {
// we already knew about some children so preserve the state of the old children
// first gather the new Children
- var newChildren = tree.ChildrenGetter(this.Model) ?? new object[0];
+ var newChildren = tree.TreeBuilder?.GetChildren(this.Model) ?? Enumerable.Empty();
// Children who no longer appear need to go
foreach(var toRemove in ChildBranches.Keys.Except(newChildren).ToArray())
@@ -800,13 +922,6 @@ namespace Terminal.Gui {
return Parent.ChildBranches.Values.LastOrDefault() == this;
}
}
-
- ///
- /// Delegates of this type are used to fetch the children of the given model object
- ///
- /// The parent whose children should be fetched
- /// An enumerable over the children
- public delegate IEnumerable ChildrenGetterDelegate(object model);
///
/// Delegates of this type are used to fetch string representations of user's model objects
@@ -814,14 +929,6 @@ namespace Terminal.Gui {
///
///
public delegate string AspectGetterDelegate(object model);
-
- ///
- /// Delegates of this type are used to quickly display to the user whether a given user object can be expanded when fetching it's children is expensive (e.g. indicating to a user that all 1000 folders can be expanded because they are folders without having to calculate contents)
- ///
- ///
- ///
- public delegate bool CanExpandGetterDelegate(object model);
-
///
/// Event arguments describing a change in selected object in a tree view
diff --git a/UICatalog/Scenarios/TreeViewFileSystem.cs b/UICatalog/Scenarios/TreeViewFileSystem.cs
index 1df6b8563..7031ab91a 100644
--- a/UICatalog/Scenarios/TreeViewFileSystem.cs
+++ b/UICatalog/Scenarios/TreeViewFileSystem.cs
@@ -73,12 +73,14 @@ namespace UICatalog.Scenarios {
///
private void SetupFileSystemDelegates ()
{
-
- // As a shortcut to enumerating half the file system, tell tree that all directories are expandable (even if they turn out to be empty later on)
- _treeView.CanExpandGetter = (o)=>o is DirectoryInfo;
+ _treeView.TreeBuilder = new DelegateTreeBuilder(
- // Determines how to compute children of any given branch
- _treeView.ChildrenGetter = GetChildren;
+ // Determines how to compute children of any given branch
+ GetChildren,
+ // As a shortcut to enumerating half the file system, tell tree that all directories are expandable (even if they turn out to be empty later on)
+ (o)=>o is DirectoryInfo
+
+ );
// Determines how to represent objects as strings on the screen
_treeView.AspectGetter = AspectGetter;
@@ -88,9 +90,8 @@ namespace UICatalog.Scenarios {
{
ClearObjects();
- // Clear any previous delegates
- _treeView.CanExpandGetter = null;
- _treeView.ChildrenGetter = null;
+ // Set builder to serve children of ITreeNode objects
+ _treeView.TreeBuilder = new TreeNodeBuilder();
// Add 2 root nodes with simple set of subfolders
_treeView.AddObject(CreateSimpleRoot());
diff --git a/UnitTests/TreeViewTests.cs b/UnitTests/TreeViewTests.cs
index 1c8daeb04..849d503fc 100644
--- a/UnitTests/TreeViewTests.cs
+++ b/UnitTests/TreeViewTests.cs
@@ -33,8 +33,7 @@ namespace UnitTests {
Cars = new []{car1 ,car2}
};
- var tree = new TreeView();
- tree.ChildrenGetter = (s)=> s is Factory f ? f.Cars: null;
+ var tree = new TreeView(new DelegateTreeBuilder((s)=> s is Factory f ? f.Cars: null));
tree.AddObject(factory1);
return tree;
@@ -219,11 +218,11 @@ namespace UnitTests {
Assert.False(tree.IsExpanded(c1));
// change the children getter so that now cars can have wheels
- tree.ChildrenGetter = (o)=>
+ tree.TreeBuilder = new DelegateTreeBuilder((o)=>
// factories have cars
o is Factory ? new object[]{c1,c2}
// cars have wheels
- : new object[]{wheel };
+ : new object[]{wheel });
// still cannot expand
tree.Expand(c1);
@@ -256,11 +255,11 @@ namespace UnitTests {
Assert.False(tree.IsExpanded(c1));
// change the children getter so that now cars can have wheels
- tree.ChildrenGetter = (o)=>
+ tree.TreeBuilder = new DelegateTreeBuilder((o)=>
// factories have cars
o is Factory ? new object[]{c1,c2}
// cars have wheels
- : new object[]{wheel };
+ : new object[]{wheel });
// still cannot expand
tree.Expand(c1);
@@ -333,7 +332,7 @@ namespace UnitTests {
string root = "root";
var tree = new TreeView();
- tree.ChildrenGetter = (s)=> ReferenceEquals(s , root) ? new object[]{obj1 } : null;
+ tree.TreeBuilder = new DelegateTreeBuilder((s)=> ReferenceEquals(s , root) ? new object[]{obj1 } : null);
tree.AddObject(root);
// Tree is not expanded so the root has no children yet
@@ -346,7 +345,7 @@ namespace UnitTests {
Assert.Equal(1,tree.GetChildren(root).Count(child=>ReferenceEquals(obj1,child)));
// change the getter to return an Equal object (but not the same reference - obj2)
- tree.ChildrenGetter = (s)=> ReferenceEquals(s , root) ? new object[]{obj2 } : null;
+ tree.TreeBuilder = new DelegateTreeBuilder((s)=> ReferenceEquals(s , root) ? new object[]{obj2 } : null);
// tree has cached the knowledge of what children the root has so won't know about the change (we still get obj1)
Assert.Equal(1,tree.GetChildren(root).Count(child=>ReferenceEquals(obj1,child)));