From cd96ac524b28393c77b742a4b2ee68298796570f Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 23 Jul 2023 14:47:07 +0100 Subject: [PATCH 1/7] Refactor to use RuneCell for drawing tree branches --- Terminal.Gui/Views/TreeView/Branch.cs | 39 +++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/Terminal.Gui/Views/TreeView/Branch.cs b/Terminal.Gui/Views/TreeView/Branch.cs index 208a648f1..a6c4e93bc 100644 --- a/Terminal.Gui/Views/TreeView/Branch.cs +++ b/Terminal.Gui/Views/TreeView/Branch.cs @@ -95,6 +95,8 @@ namespace Terminal.Gui { /// public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, int availableWidth) { + var cells = new List(); + // true if the current line of the tree is the selected one and control has focus bool isSelected = tree.IsSelected (Model); @@ -110,15 +112,15 @@ namespace Terminal.Gui { // if we have scrolled to the right then bits of the prefix will have dispeared off the screen int toSkip = tree.ScrollOffsetHorizontal; + var attr = symbolColor; - driver.SetAttribute (symbolColor); // Draw the line prefix (all parallel lanes or whitespace and an expand/collapse/leaf symbol) foreach (Rune r in prefix) { if (toSkip > 0) { toSkip--; } else { - driver.AddRune (r); + cells.Add(NewRuneCell(attr,r)); availableWidth -= r.GetColumns (); } } @@ -141,13 +143,14 @@ namespace Terminal.Gui { color = new Attribute (color.Background, color.Foreground); } - driver.SetAttribute (color); + attr = color; } if (toSkip > 0) { toSkip--; } else { - driver.AddRune (expansion); + + cells.Add(NewRuneCell(attr,expansion)); availableWidth -= expansion.GetColumns (); } @@ -186,16 +189,36 @@ namespace Terminal.Gui { } } - driver.SetAttribute (modelColor); - driver.AddStr (lineBody); + attr = modelColor; + cells.AddRange(lineBody.Select(r=>NewRuneCell(attr,new Rune(r)))); if (availableWidth > 0) { - driver.SetAttribute (symbolColor); - driver.AddStr (new string (' ', availableWidth)); + attr = symbolColor; + cells.AddRange( + Enumerable.Repeat( + NewRuneCell(attr,new Rune(' ')), + availableWidth + )); } + + foreach(var cell in cells) + { + driver.SetAttribute(cell.ColorScheme.Normal); + driver.AddRune(cell.Rune); + } + driver.SetAttribute (colorScheme.Normal); } + private static RuneCell NewRuneCell (Attribute attr, Rune r) + { + return new RuneCell{ + Rune = r, + ColorScheme = new ColorScheme(attr) + }; + } + + /// /// Gets all characters to render prior to the current branches line. This includes indentation /// whitespace and any tree branches (if enabled). From 2f1db90fcd5ad7098d6c349f66bf4cceff38845f Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 23 Jul 2023 15:05:22 +0100 Subject: [PATCH 2/7] Added DrawLine event to TreeView --- Terminal.Gui/Views/TreeView/Branch.cs | 19 +++++++-- Terminal.Gui/Views/TreeView/TreeView.cs | 56 +++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/Views/TreeView/Branch.cs b/Terminal.Gui/Views/TreeView/Branch.cs index a6c4e93bc..03839d5e4 100644 --- a/Terminal.Gui/Views/TreeView/Branch.cs +++ b/Terminal.Gui/Views/TreeView/Branch.cs @@ -201,10 +201,21 @@ namespace Terminal.Gui { )); } - foreach(var cell in cells) - { - driver.SetAttribute(cell.ColorScheme.Normal); - driver.AddRune(cell.Rune); + var e = new DrawTreeViewLineEventArgs{ + Model = Model, + Y = y, + RuneCells = cells, + Tree = tree + }; + tree.OnDrawLine(e); + + if(!e.Handled) + { + foreach(var cell in cells) + { + driver.SetAttribute(cell.ColorScheme.Normal); + driver.AddRune(cell.Rune); + } } driver.SetAttribute (colorScheme.Normal); diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs index 318b0ffae..5dc30c58d 100644 --- a/Terminal.Gui/Views/TreeView/TreeView.cs +++ b/Terminal.Gui/Views/TreeView/TreeView.cs @@ -169,6 +169,12 @@ namespace Terminal.Gui { /// public event EventHandler> SelectionChanged; + /// + /// Called once for each visible row during rendering. Can be used + /// to make last minute changes to color or text rendered + /// + public event EventHandler> DrawLine; + /// /// The root objects in the tree, note that this collection is of root objects only. /// @@ -1421,8 +1427,58 @@ namespace Terminal.Gui { { SelectionChanged?.Invoke (this, e); } + + /// + /// Raises the DrawLine event + /// + /// + internal void OnDrawLine (DrawTreeViewLineEventArgs e) + { + DrawLine?.Invoke(this,e); + } + } + /// + /// Event args for the event + /// + /// + public class DrawTreeViewLineEventArgs where T : class{ + + /// + /// The object at this line in the tree + /// + public T Model {get;init;} + + /// + /// The that is performing the + /// rendering. + /// + public TreeView Tree {get; init;} + + /// + /// The line within tree view bounds that is being rendered + /// + public int Y {get;init;} + + /// + /// Set to true to cancel drawing (e.g. if you have already manually + /// drawn content). + /// + public bool Handled {get;set;} + + /// + /// The rune and color of each symbol that will be rendered. Note + /// that only is respected. You + /// can modify these to change what is rendered. + /// + /// + /// Changing the length of this collection may result in corrupt rendering + /// + public List RuneCells {get; init;} + } + + class TreeSelection where T : class { public Branch Origin { get; } From 95e0d72bda074a066f056ab51c603745aeb785c6 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 23 Jul 2023 15:27:16 +0100 Subject: [PATCH 3/7] Use the new DrawLine event in file system tree to color folder icons --- Terminal.Gui/Views/TreeView/Branch.cs | 18 ++++++++++++++++-- Terminal.Gui/Views/TreeView/TreeView.cs | 20 ++++++++++++++++++++ UICatalog/Scenarios/TreeViewFileSystem.cs | 21 +++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Views/TreeView/Branch.cs b/Terminal.Gui/Views/TreeView/Branch.cs index 03839d5e4..7d4bcc7f3 100644 --- a/Terminal.Gui/Views/TreeView/Branch.cs +++ b/Terminal.Gui/Views/TreeView/Branch.cs @@ -96,6 +96,9 @@ namespace Terminal.Gui { public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, int availableWidth) { var cells = new List(); + int? indexOfExpandCollapseSymbol = null; + int indexOfModelText; + // true if the current line of the tree is the selected one and control has focus bool isSelected = tree.IsSelected (Model); @@ -149,19 +152,28 @@ namespace Terminal.Gui { if (toSkip > 0) { toSkip--; } else { - + indexOfExpandCollapseSymbol = cells.Count; cells.Add(NewRuneCell(attr,expansion)); availableWidth -= expansion.GetColumns (); } // horizontal scrolling has already skipped the prefix but now must also skip some of the line body if (toSkip > 0) { + + // For the event record a negative location for where model text starts since it + // is pushed off to the left because of scrolling + indexOfModelText = -toSkip; + if (toSkip > lineBody.Length) { lineBody = ""; } else { lineBody = lineBody.Substring (toSkip); } } + else + { + indexOfModelText = cells.Count; + } // If body of line is too long if (lineBody.EnumerateRunes ().Sum (l => l.GetColumns ()) > availableWidth) { @@ -205,7 +217,9 @@ namespace Terminal.Gui { Model = Model, Y = y, RuneCells = cells, - Tree = tree + Tree = tree, + IndexOfExpandCollapseSymbol = indexOfExpandCollapseSymbol, + IndexOfModelText = indexOfModelText, }; tree.OnDrawLine(e); diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs index 5dc30c58d..b959fdaff 100644 --- a/Terminal.Gui/Views/TreeView/TreeView.cs +++ b/Terminal.Gui/Views/TreeView/TreeView.cs @@ -1476,6 +1476,26 @@ namespace Terminal.Gui { /// Changing the length of this collection may result in corrupt rendering /// public List RuneCells {get; init;} + + /// + /// The notional index in which contains the first + /// character of the text (i.e. + /// after all branch lines and expansion/collapse sybmols). + /// + /// + /// May be negative or outside of bounds of if the view + /// has been scrolled horizontally. + /// + + public int IndexOfModelText {get;init;} + + /// + /// If line contains a branch that can be expanded/collapsed then this is + /// the index in at which the symbol is (or null for + /// leaf elements). + /// + public int? IndexOfExpandCollapseSymbol {get;init;} + } diff --git a/UICatalog/Scenarios/TreeViewFileSystem.cs b/UICatalog/Scenarios/TreeViewFileSystem.cs index ddae1dda1..d0cd8078c 100644 --- a/UICatalog/Scenarios/TreeViewFileSystem.cs +++ b/UICatalog/Scenarios/TreeViewFileSystem.cs @@ -84,6 +84,7 @@ namespace UICatalog.Scenarios { Width = Dim.Percent (50), Height = Dim.Fill (), }; + treeViewFiles.DrawLine += TreeViewFiles_DrawLine; _detailsFrame = new DetailsFrame (_iconProvider) { X = Pos.Right (treeViewFiles), @@ -140,6 +141,26 @@ namespace UICatalog.Scenarios { ShowPropertiesOf (e.NewValue); } + private void TreeViewFiles_DrawLine (object sender, DrawTreeViewLineEventArgs e) + { + // Render directory icons in yellow + if(e.Model is IDirectoryInfo d) + { + if(_iconProvider.UseNerdIcons || _iconProvider.UseUnicodeCharacters) + { + if(e.IndexOfModelText > 0 && e.IndexOfModelText < e.RuneCells.Count) + { + var cell = e.RuneCells[e.IndexOfModelText]; + cell.ColorScheme = new ColorScheme( + new Terminal.Gui.Attribute( + Color.BrightYellow, + cell.ColorScheme.Normal.Background) + ); + } + } + } + } + private void TreeViewFiles_KeyPress (object sender, KeyEventEventArgs obj) { if (obj.KeyEvent.Key == (Key.R | Key.CtrlMask)) { From 897b3a3eea69e432e952d0dcf045199c21c75c44 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 23 Jul 2023 17:25:56 +0100 Subject: [PATCH 4/7] Layout tidy up and whitespace fixes --- Terminal.Gui/Views/TreeView/Branch.cs | 45 ++++----- .../TreeView/DrawTreeViewLineEventArgs.cs | 66 +++++++++++++ Terminal.Gui/Views/TreeView/TreeView.cs | 99 ++++--------------- UICatalog/Scenarios/TreeViewFileSystem.cs | 37 ++++--- 4 files changed, 120 insertions(+), 127 deletions(-) create mode 100644 Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs diff --git a/Terminal.Gui/Views/TreeView/Branch.cs b/Terminal.Gui/Views/TreeView/Branch.cs index 7d4bcc7f3..83acdf745 100644 --- a/Terminal.Gui/Views/TreeView/Branch.cs +++ b/Terminal.Gui/Views/TreeView/Branch.cs @@ -65,11 +65,10 @@ namespace Terminal.Gui { if (Depth >= tree.MaxDepth) { children = Enumerable.Empty (); - } - else { + } else { children = tree.TreeBuilder.GetChildren (this.Model) ?? Enumerable.Empty (); } - + this.ChildBranches = children.ToDictionary (k => k, val => new Branch (tree, this, val)); } @@ -95,10 +94,10 @@ namespace Terminal.Gui { /// public virtual void Draw (ConsoleDriver driver, ColorScheme colorScheme, int y, int availableWidth) { - var cells = new List(); + var cells = new List (); int? indexOfExpandCollapseSymbol = null; int indexOfModelText; - + // true if the current line of the tree is the selected one and control has focus bool isSelected = tree.IsSelected (Model); @@ -123,7 +122,7 @@ namespace Terminal.Gui { if (toSkip > 0) { toSkip--; } else { - cells.Add(NewRuneCell(attr,r)); + cells.Add (NewRuneCell (attr, r)); availableWidth -= r.GetColumns (); } } @@ -153,13 +152,13 @@ namespace Terminal.Gui { toSkip--; } else { indexOfExpandCollapseSymbol = cells.Count; - cells.Add(NewRuneCell(attr,expansion)); + cells.Add (NewRuneCell (attr, expansion)); availableWidth -= expansion.GetColumns (); } // horizontal scrolling has already skipped the prefix but now must also skip some of the line body if (toSkip > 0) { - + // For the event record a negative location for where model text starts since it // is pushed off to the left because of scrolling indexOfModelText = -toSkip; @@ -169,9 +168,7 @@ namespace Terminal.Gui { } else { lineBody = lineBody.Substring (toSkip); } - } - else - { + } else { indexOfModelText = cells.Count; } @@ -202,18 +199,18 @@ namespace Terminal.Gui { } attr = modelColor; - cells.AddRange(lineBody.Select(r=>NewRuneCell(attr,new Rune(r)))); + cells.AddRange (lineBody.Select (r => NewRuneCell (attr, new Rune (r)))); if (availableWidth > 0) { attr = symbolColor; - cells.AddRange( - Enumerable.Repeat( - NewRuneCell(attr,new Rune(' ')), + cells.AddRange ( + Enumerable.Repeat ( + NewRuneCell (attr, new Rune (' ')), availableWidth )); } - var e = new DrawTreeViewLineEventArgs{ + var e = new DrawTreeViewLineEventArgs { Model = Model, Y = y, RuneCells = cells, @@ -221,14 +218,12 @@ namespace Terminal.Gui { IndexOfExpandCollapseSymbol = indexOfExpandCollapseSymbol, IndexOfModelText = indexOfModelText, }; - tree.OnDrawLine(e); + tree.OnDrawLine (e); - if(!e.Handled) - { - foreach(var cell in cells) - { - driver.SetAttribute(cell.ColorScheme.Normal); - driver.AddRune(cell.Rune); + if (!e.Handled) { + foreach (var cell in cells) { + driver.SetAttribute (cell.ColorScheme.Normal); + driver.AddRune (cell.Rune); } } @@ -237,9 +232,9 @@ namespace Terminal.Gui { private static RuneCell NewRuneCell (Attribute attr, Rune r) { - return new RuneCell{ + return new RuneCell { Rune = r, - ColorScheme = new ColorScheme(attr) + ColorScheme = new ColorScheme (attr) }; } diff --git a/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs b/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs new file mode 100644 index 000000000..16b90f392 --- /dev/null +++ b/Terminal.Gui/Views/TreeView/DrawTreeViewLineEventArgs.cs @@ -0,0 +1,66 @@ +// This code is based on http://objectlistview.sourceforge.net (GPLv3 tree/list controls +// by phillip.piper@gmail.com). Phillip has explicitly granted permission for his design +// and code to be used in this library under the MIT license. + +using System.Collections.Generic; + +namespace Terminal.Gui { + /// + /// Event args for the event + /// + /// + public class DrawTreeViewLineEventArgs where T : class { + + /// + /// The object at this line in the tree + /// + public T Model { get; init; } + + /// + /// The that is performing the + /// rendering. + /// + public TreeView Tree { get; init; } + + /// + /// The line within tree view bounds that is being rendered + /// + public int Y { get; init; } + + /// + /// Set to true to cancel drawing (e.g. if you have already manually + /// drawn content). + /// + public bool Handled { get; set; } + + /// + /// The rune and color of each symbol that will be rendered. Note + /// that only is respected. You + /// can modify these to change what is rendered. + /// + /// + /// Changing the length of this collection may result in corrupt rendering + /// + public List RuneCells { get; init; } + + /// + /// The notional index in which contains the first + /// character of the text (i.e. + /// after all branch lines and expansion/collapse sybmols). + /// + /// + /// May be negative or outside of bounds of if the view + /// has been scrolled horizontally. + /// + + public int IndexOfModelText { get; init; } + + /// + /// If line contains a branch that can be expanded/collapsed then this is + /// the index in at which the symbol is (or null for + /// leaf elements). + /// + public int? IndexOfExpandCollapseSymbol { get; init; } + + } +} \ No newline at end of file diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs index b959fdaff..ba6394411 100644 --- a/Terminal.Gui/Views/TreeView/TreeView.cs +++ b/Terminal.Gui/Views/TreeView/TreeView.cs @@ -563,10 +563,9 @@ namespace Terminal.Gui { List> toReturn = new List> (); foreach (var root in roots.Values) { - + var toAdd = AddToLineMap (root, false, out var isMatch); - if(isMatch) - { + if (isMatch) { toReturn.AddRange (toAdd); } } @@ -580,41 +579,38 @@ namespace Terminal.Gui { private bool IsFilterMatch (Branch branch) { - return Filter?.IsMatch(branch.Model) ?? true; + return Filter?.IsMatch (branch.Model) ?? true; } - private IEnumerable> AddToLineMap (Branch currentBranch,bool parentMatches, out bool match) + private IEnumerable> AddToLineMap (Branch currentBranch, bool parentMatches, out bool match) { - bool weMatch = IsFilterMatch(currentBranch); + bool weMatch = IsFilterMatch (currentBranch); bool anyChildMatches = false; - - var toReturn = new List>(); - var children = new List>(); + + var toReturn = new List> (); + var children = new List> (); if (currentBranch.IsExpanded) { foreach (var subBranch in currentBranch.ChildBranches.Values) { foreach (var sub in AddToLineMap (subBranch, weMatch, out var childMatch)) { - - if(childMatch) - { - children.Add(sub); + + if (childMatch) { + children.Add (sub); anyChildMatches = true; } } } } - if(parentMatches || weMatch || anyChildMatches) - { + if (parentMatches || weMatch || anyChildMatches) { match = true; - toReturn.Add(currentBranch); - } - else{ + toReturn.Add (currentBranch); + } else { match = false; } - - toReturn.AddRange(children); + + toReturn.AddRange (children); return toReturn; } @@ -1434,71 +1430,10 @@ namespace Terminal.Gui { /// internal void OnDrawLine (DrawTreeViewLineEventArgs e) { - DrawLine?.Invoke(this,e); + DrawLine?.Invoke (this, e); } } - - /// - /// Event args for the event - /// - /// - public class DrawTreeViewLineEventArgs where T : class{ - - /// - /// The object at this line in the tree - /// - public T Model {get;init;} - - /// - /// The that is performing the - /// rendering. - /// - public TreeView Tree {get; init;} - - /// - /// The line within tree view bounds that is being rendered - /// - public int Y {get;init;} - - /// - /// Set to true to cancel drawing (e.g. if you have already manually - /// drawn content). - /// - public bool Handled {get;set;} - - /// - /// The rune and color of each symbol that will be rendered. Note - /// that only is respected. You - /// can modify these to change what is rendered. - /// - /// - /// Changing the length of this collection may result in corrupt rendering - /// - public List RuneCells {get; init;} - - /// - /// The notional index in which contains the first - /// character of the text (i.e. - /// after all branch lines and expansion/collapse sybmols). - /// - /// - /// May be negative or outside of bounds of if the view - /// has been scrolled horizontally. - /// - - public int IndexOfModelText {get;init;} - - /// - /// If line contains a branch that can be expanded/collapsed then this is - /// the index in at which the symbol is (or null for - /// leaf elements). - /// - public int? IndexOfExpandCollapseSymbol {get;init;} - - } - - class TreeSelection where T : class { public Branch Origin { get; } diff --git a/UICatalog/Scenarios/TreeViewFileSystem.cs b/UICatalog/Scenarios/TreeViewFileSystem.cs index d0cd8078c..0f5cc497f 100644 --- a/UICatalog/Scenarios/TreeViewFileSystem.cs +++ b/UICatalog/Scenarios/TreeViewFileSystem.cs @@ -107,7 +107,7 @@ namespace UICatalog.Scenarios { SetupScrollBar (); treeViewFiles.SetFocus (); - + UpdateIconCheckedness (); } @@ -144,15 +144,12 @@ namespace UICatalog.Scenarios { private void TreeViewFiles_DrawLine (object sender, DrawTreeViewLineEventArgs e) { // Render directory icons in yellow - if(e.Model is IDirectoryInfo d) - { - if(_iconProvider.UseNerdIcons || _iconProvider.UseUnicodeCharacters) - { - if(e.IndexOfModelText > 0 && e.IndexOfModelText < e.RuneCells.Count) - { - var cell = e.RuneCells[e.IndexOfModelText]; - cell.ColorScheme = new ColorScheme( - new Terminal.Gui.Attribute( + if (e.Model is IDirectoryInfo d) { + if (_iconProvider.UseNerdIcons || _iconProvider.UseUnicodeCharacters) { + if (e.IndexOfModelText > 0 && e.IndexOfModelText < e.RuneCells.Count) { + var cell = e.RuneCells [e.IndexOfModelText]; + cell.ColorScheme = new ColorScheme ( + new Terminal.Gui.Attribute ( Color.BrightYellow, cell.ColorScheme.Normal.Background) ); @@ -216,7 +213,7 @@ namespace UICatalog.Scenarios { private IFileSystemInfo fileInfo; private FileSystemIconProvider _iconProvider; - public DetailsFrame (FileSystemIconProvider iconProvider) + public DetailsFrame (FileSystemIconProvider iconProvider) { Title = "Details"; Visible = true; @@ -230,7 +227,7 @@ namespace UICatalog.Scenarios { System.Text.StringBuilder sb = null; if (fileInfo is IFileInfo f) { - Title = $"{_iconProvider.GetIconWithOptionalSpace(f)}{f.Name}".Trim(); + Title = $"{_iconProvider.GetIconWithOptionalSpace (f)}{f.Name}".Trim (); sb = new System.Text.StringBuilder (); sb.AppendLine ($"Path:\n {f.FullName}\n"); sb.AppendLine ($"Size:\n {f.Length:N0} bytes\n"); @@ -239,7 +236,7 @@ namespace UICatalog.Scenarios { } if (fileInfo is IDirectoryInfo dir) { - Title = $"{_iconProvider.GetIconWithOptionalSpace(dir)}{dir.Name}".Trim(); + Title = $"{_iconProvider.GetIconWithOptionalSpace (dir)}{dir.Name}".Trim (); sb = new System.Text.StringBuilder (); sb.AppendLine ($"Path:\n {dir?.FullName}\n"); sb.AppendLine ($"Modified:\n {dir.LastWriteTime}\n"); @@ -262,7 +259,7 @@ namespace UICatalog.Scenarios { var scrollBar = new ScrollBarView (treeViewFiles, true); - scrollBar.ChangedPosition += (s,e) => { + scrollBar.ChangedPosition += (s, e) => { treeViewFiles.ScrollOffsetVertical = scrollBar.Position; if (treeViewFiles.ScrollOffsetVertical != scrollBar.Position) { scrollBar.Position = treeViewFiles.ScrollOffsetVertical; @@ -270,7 +267,7 @@ namespace UICatalog.Scenarios { treeViewFiles.SetNeedsDisplay (); }; - scrollBar.OtherScrollBarView.ChangedPosition += (s,e) => { + scrollBar.OtherScrollBarView.ChangedPosition += (s, e) => { treeViewFiles.ScrollOffsetHorizontal = scrollBar.OtherScrollBarView.Position; if (treeViewFiles.ScrollOffsetHorizontal != scrollBar.OtherScrollBarView.Position) { scrollBar.OtherScrollBarView.Position = treeViewFiles.ScrollOffsetHorizontal; @@ -278,7 +275,7 @@ namespace UICatalog.Scenarios { treeViewFiles.SetNeedsDisplay (); }; - treeViewFiles.DrawContent += (s,e) => { + treeViewFiles.DrawContent += (s, e) => { scrollBar.Size = treeViewFiles.ContentHeight; scrollBar.Position = treeViewFiles.ScrollOffsetVertical; scrollBar.OtherScrollBarView.Size = treeViewFiles.GetContentWidth (true); @@ -290,20 +287,20 @@ namespace UICatalog.Scenarios { private void SetupFileTree () { // setup how to build tree - var fs = new FileSystem(); - var rootDirs = DriveInfo.GetDrives ().Select (d=>fs.DirectoryInfo.New(d.RootDirectory.FullName)); + var fs = new FileSystem (); + var rootDirs = DriveInfo.GetDrives ().Select (d => fs.DirectoryInfo.New (d.RootDirectory.FullName)); treeViewFiles.TreeBuilder = new FileSystemTreeBuilder (); treeViewFiles.AddObjects (rootDirs); // Determines how to represent objects as strings on the screen treeViewFiles.AspectGetter = AspectGetter; - + _iconProvider.IsOpenGetter = treeViewFiles.IsExpanded; } private string AspectGetter (IFileSystemInfo f) { - return (_iconProvider.GetIconWithOptionalSpace(f) + f.Name).Trim(); + return (_iconProvider.GetIconWithOptionalSpace (f) + f.Name).Trim (); } private void ShowLines () From 78eebd1ce7e6310d9e0274277f60fe51b67bbaa9 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 23 Jul 2023 17:40:14 +0100 Subject: [PATCH 5/7] Add test TestTreeView_DrawLineEvent --- UnitTests/Views/TreeViewTests.cs | 69 +++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/UnitTests/Views/TreeViewTests.cs b/UnitTests/Views/TreeViewTests.cs index 0644fac65..42c89e832 100644 --- a/UnitTests/Views/TreeViewTests.cs +++ b/UnitTests/Views/TreeViewTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using Xunit; using Xunit.Abstractions; @@ -939,7 +940,7 @@ namespace Terminal.Gui.ViewsTests { Assert.False (tv.CanExpand ("6")); Assert.False (tv.IsExpanded ("6")); - tv.Collapse("6"); + tv.Collapse ("6"); Assert.False (tv.CanExpand ("6")); Assert.False (tv.IsExpanded ("6")); @@ -994,7 +995,71 @@ namespace Terminal.Gui.ViewsTests { └─4 ", output); } + [Fact, AutoInitShutdown] + public void TestTreeView_DrawLineEvent () + { + var tv = new TreeView { Width = 20, Height = 10 }; + var eventArgs = new List> (); + + tv.DrawLine += (s, e) => { + eventArgs.Add (e); + }; + + var n1 = new TreeNode ("root one"); + var n1_1 = new TreeNode ("leaf 1"); + var n1_2 = new TreeNode ("leaf 2"); + n1.Children.Add (n1_1); + n1.Children.Add (n1_2); + + var n2 = new TreeNode ("root two"); + tv.AddObject (n1); + tv.AddObject (n2); + tv.Expand (n1); + + tv.ColorScheme = new ColorScheme (); + tv.LayoutSubviews (); + tv.Draw (); + + // Normal drawing of the tree view + TestHelpers.AssertDriverContentsAre ( +@" +├-root one +│ ├─leaf 1 +│ └─leaf 2 +└─root two +", output); + Assert.Equal (4, eventArgs.Count ()); + + Assert.Equal (0, eventArgs [0].Y); + Assert.Equal (1, eventArgs [1].Y); + Assert.Equal (2, eventArgs [2].Y); + Assert.Equal (3, eventArgs [3].Y); + + Assert.All (eventArgs, ea => Assert.Equal (ea.Tree, tv)); + Assert.All (eventArgs, ea => Assert.False (ea.Handled)); + + Assert.Equal ("├-root one", eventArgs [0].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("│ ├─leaf 1", eventArgs [1].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("│ └─leaf 2", eventArgs [2].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("└─root two", eventArgs [3].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + + Assert.Equal (1,eventArgs [0].IndexOfExpandCollapseSymbol); + Assert.Equal (3, eventArgs [1].IndexOfExpandCollapseSymbol); + Assert.Equal (3, eventArgs [2].IndexOfExpandCollapseSymbol); + Assert.Equal (1, eventArgs [3].IndexOfExpandCollapseSymbol); + + Assert.Equal (2, eventArgs [0].IndexOfModelText); + Assert.Equal (4, eventArgs [1].IndexOfModelText); + Assert.Equal (4, eventArgs [2].IndexOfModelText); + Assert.Equal (2, eventArgs [3].IndexOfModelText); + + + Assert.Equal ("root one", eventArgs [0].Model.Text); + Assert.Equal ("leaf 1", eventArgs [1].Model.Text); + Assert.Equal ("leaf 2", eventArgs [2].Model.Text); + Assert.Equal ("root two", eventArgs [3].Model.Text); + } [Fact, AutoInitShutdown] public void TestTreeView_Filter () { From 101db86bfa37cd99ec53d2c7fa260f511dee3e04 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 23 Jul 2023 17:43:36 +0100 Subject: [PATCH 6/7] Add TestTreeView_DrawLineEvent_WithScrolling test --- UnitTests/Views/TreeViewTests.cs | 65 +++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/UnitTests/Views/TreeViewTests.cs b/UnitTests/Views/TreeViewTests.cs index 42c89e832..bf8ff84e3 100644 --- a/UnitTests/Views/TreeViewTests.cs +++ b/UnitTests/Views/TreeViewTests.cs @@ -1044,7 +1044,7 @@ namespace Terminal.Gui.ViewsTests { Assert.Equal ("│ └─leaf 2", eventArgs [2].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); Assert.Equal ("└─root two", eventArgs [3].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal (1,eventArgs [0].IndexOfExpandCollapseSymbol); + Assert.Equal (1, eventArgs [0].IndexOfExpandCollapseSymbol); Assert.Equal (3, eventArgs [1].IndexOfExpandCollapseSymbol); Assert.Equal (3, eventArgs [2].IndexOfExpandCollapseSymbol); Assert.Equal (1, eventArgs [3].IndexOfExpandCollapseSymbol); @@ -1060,6 +1060,69 @@ namespace Terminal.Gui.ViewsTests { Assert.Equal ("leaf 2", eventArgs [2].Model.Text); Assert.Equal ("root two", eventArgs [3].Model.Text); } + + [Fact, AutoInitShutdown] + public void TestTreeView_DrawLineEvent_WithScrolling () + { + var tv = new TreeView { Width = 20, Height = 10 }; + + var eventArgs = new List> (); + + tv.DrawLine += (s, e) => { + eventArgs.Add (e); + }; + + tv.ScrollOffsetHorizontal = 3; + tv.ScrollOffsetVertical = 1; + + var n1 = new TreeNode ("root one"); + var n1_1 = new TreeNode ("leaf 1"); + var n1_2 = new TreeNode ("leaf 2"); + n1.Children.Add (n1_1); + n1.Children.Add (n1_2); + + var n2 = new TreeNode ("root two"); + tv.AddObject (n1); + tv.AddObject (n2); + tv.Expand (n1); + + tv.ColorScheme = new ColorScheme (); + tv.LayoutSubviews (); + tv.Draw (); + + // Normal drawing of the tree view + TestHelpers.AssertDriverContentsAre ( +@" +─leaf 1 +─leaf 2 +oot two +", output); + Assert.Equal (3, eventArgs.Count ()); + + Assert.Equal (0, eventArgs [0].Y); + Assert.Equal (1, eventArgs [1].Y); + Assert.Equal (2, eventArgs [2].Y); + + Assert.All (eventArgs, ea => Assert.Equal (ea.Tree, tv)); + Assert.All (eventArgs, ea => Assert.False (ea.Handled)); + + Assert.Equal ("─leaf 1", eventArgs [0].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("─leaf 2", eventArgs [1].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("oot two", eventArgs [2].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + + Assert.Equal (0, eventArgs [0].IndexOfExpandCollapseSymbol); + Assert.Equal (0, eventArgs [1].IndexOfExpandCollapseSymbol); + Assert.Null (eventArgs [2].IndexOfExpandCollapseSymbol); + + Assert.Equal (1, eventArgs [0].IndexOfModelText); + Assert.Equal (1, eventArgs [1].IndexOfModelText); + Assert.Equal (-1, eventArgs [2].IndexOfModelText); + + Assert.Equal ("leaf 1", eventArgs [0].Model.Text); + Assert.Equal ("leaf 2", eventArgs [1].Model.Text); + Assert.Equal ("root two", eventArgs [2].Model.Text); + } + [Fact, AutoInitShutdown] public void TestTreeView_Filter () { From 4dbf2f4cfd5aeb8488a9cb9f8f268339631979e2 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 23 Jul 2023 17:47:18 +0100 Subject: [PATCH 7/7] Add test for marking event Handled and demo how to do custom drawing --- UnitTests/Views/TreeViewTests.cs | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/UnitTests/Views/TreeViewTests.cs b/UnitTests/Views/TreeViewTests.cs index bf8ff84e3..cf6f5e212 100644 --- a/UnitTests/Views/TreeViewTests.cs +++ b/UnitTests/Views/TreeViewTests.cs @@ -1123,6 +1123,47 @@ oot two Assert.Equal ("root two", eventArgs [2].Model.Text); } + [Fact, AutoInitShutdown] + public void TestTreeView_DrawLineEvent_Handled () + { + var tv = new TreeView { Width = 20, Height = 10 }; + + tv.DrawLine += (s, e) => { + if(e.Model.Text.Equals("leaf 1")) { + e.Handled = true; + + for (int i = 0; i < 10; i++) { + + e.Tree.AddRune (i,e.Y,new System.Text.Rune('F')); + } + } + }; + + var n1 = new TreeNode ("root one"); + var n1_1 = new TreeNode ("leaf 1"); + var n1_2 = new TreeNode ("leaf 2"); + n1.Children.Add (n1_1); + n1.Children.Add (n1_2); + + var n2 = new TreeNode ("root two"); + tv.AddObject (n1); + tv.AddObject (n2); + tv.Expand (n1); + + tv.ColorScheme = new ColorScheme (); + tv.LayoutSubviews (); + tv.Draw (); + + // Normal drawing of the tree view + TestHelpers.AssertDriverContentsAre ( +@" +├-root one +FFFFFFFFFF +│ └─leaf 2 +└─root two +", output); + } + [Fact, AutoInitShutdown] public void TestTreeView_Filter () {