diff --git a/Terminal.Gui/Views/TreeView.cs b/Terminal.Gui/Views/TreeView.cs index bd7e3f52b..0b171ec3d 100644 --- a/Terminal.Gui/Views/TreeView.cs +++ b/Terminal.Gui/Views/TreeView.cs @@ -26,6 +26,7 @@ namespace Terminal.Gui { private ChildrenGetterDelegate childrenGetter; private CanExpandGetterDelegate canExpandGetter; + private int scrollOffset; /// /// 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) @@ -51,7 +52,7 @@ namespace Terminal.Gui { selectedObject = value; if(!ReferenceEquals(oldValue,value)) - SelectionChanged?.Invoke(this,new SelectionChangedEventArgs(this,oldValue,value)); + SelectionChanged?.Invoke(this,new SelectionChangedEventArgs(this,oldValue,value)); } } @@ -74,7 +75,14 @@ namespace Terminal.Gui { /// /// The amount of tree view that has been scrolled off the top of the screen (by the user scrolling down) /// - public int ScrollOffset {get; private set;} + /// Setting a value of less than 0 will result in a ScrollOffset of 0. To see changes in the UI call + public int ScrollOffset { + get => scrollOffset; + set { + scrollOffset = Math.Max(0,value); + } + } + /// /// Creates a new tree view with absolute positioning. Use to set set root objects for the tree @@ -173,6 +181,25 @@ namespace Terminal.Gui { } } + + /// + /// Returns the index of the object if it is currently exposed (it's parent(s) have been expanded). This can be used with and to scroll to a specific object + /// + /// Uses the Equals method and returns the first index at which the object is found or -1 if it is not found + /// An object that appears in your tree and is currently exposed + /// The index the object was found at or -1 if it is not currently revealed or not in the tree at all + public int GetScrollOffsetOf(object o) + { + var map = BuildLineMap(); + for (int i = 0; i < map.Length; i++) + { + if (map[i].Model.Equals(o)) + return i; + } + + //object not found + return -1; + } /// /// Calculates all currently visible/expanded branches (including leafs) and outputs them by index from the top of the screen diff --git a/UnitTests/TreeViewTests.cs b/UnitTests/TreeViewTests.cs new file mode 100644 index 000000000..53ff9f043 --- /dev/null +++ b/UnitTests/TreeViewTests.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terminal.Gui; +using Xunit; + +namespace UnitTests { + public class TreeViewTests + { + #region Test Setup Methods + class Factory + { + public Car[] Cars {get;set;} + }; + class Car { + + }; + + private TreeView CreateTree() + { + return CreateTree(out _, out _, out _); + } + + private TreeView CreateTree(out Factory factory1, out Car car1, out Car car2) + { + car1 = new Car(); + car2 = new Car(); + + factory1 = new Factory() + { + Cars = new []{car1 ,car2} + }; + + var tree = new TreeView(); + tree.ChildrenGetter = (s)=> s is Factory f ? f.Cars: null; + tree.AddObject(factory1); + + return tree; + } + #endregion + + [Fact] + public void ScrollOffset_CannotBeNegative() + { + var tree = CreateTree(); + + Assert.Equal(0,tree.ScrollOffset); + + tree.ScrollOffset = -100; + Assert.Equal(0,tree.ScrollOffset); + + tree.ScrollOffset = 10; + Assert.Equal(10,tree.ScrollOffset); + } + + + [Fact] + public void GetScrollOffsetOf_MinusOneForUnRevealed() + { + var tree = CreateTree(out Factory f, out Car c1, out Car c2); + + Assert.Equal(0,tree.GetScrollOffsetOf(f)); + Assert.Equal(-1,tree.GetScrollOffsetOf(c1)); + Assert.Equal(-1,tree.GetScrollOffsetOf(c2)); + + //reveal it by expanding the root object + tree.Expand(f); + + Assert.Equal(0,tree.GetScrollOffsetOf(f)); + Assert.Equal(1,tree.GetScrollOffsetOf(c1)); + Assert.Equal(2,tree.GetScrollOffsetOf(c2)); + + tree.Collapse(f); + + Assert.Equal(0,tree.GetScrollOffsetOf(f)); + Assert.Equal(-1,tree.GetScrollOffsetOf(c1)); + Assert.Equal(-1,tree.GetScrollOffsetOf(c2)); + } + } +}