From 2d7b3f4f520692ba4e9b51f4c377b937de2b2d92 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 21 Feb 2021 12:52:42 +0000 Subject: [PATCH] Added interactive scenario (create and delete tree nodes on demand). Added Tag and Text to interface for ITreeNode --- Terminal.Gui/Views/TreeView.cs | 16 +++ UICatalog/Scenarios/InteractiveTree.cs | 150 +++++++++++++++++++++++++ UICatalog/Scenarios/TreeUseCases.cs | 8 +- docfx/articles/treeview.md | 4 +- 4 files changed, 170 insertions(+), 8 deletions(-) create mode 100644 UICatalog/Scenarios/InteractiveTree.cs diff --git a/Terminal.Gui/Views/TreeView.cs b/Terminal.Gui/Views/TreeView.cs index 144b9c502..e5bc56b37 100644 --- a/Terminal.Gui/Views/TreeView.cs +++ b/Terminal.Gui/Views/TreeView.cs @@ -12,11 +12,21 @@ namespace Terminal.Gui { /// public interface ITreeNode { + /// + /// Text to display when rendering the node + /// + string Text {get;set;} + /// /// The children of your class which should be rendered underneath it when expanded /// /// IList Children {get;} + + /// + /// Optionally allows you to store some custom data/class here. + /// + object Tag {get;set;} } /// @@ -36,6 +46,11 @@ namespace Terminal.Gui { /// public string Text {get;set;} + /// + /// Optionally allows you to store some custom data/class here. + /// + public object Tag {get;set;} + /// /// returns /// @@ -228,6 +243,7 @@ namespace Terminal.Gui { public TreeView () { TreeBuilder = new TreeNodeBuilder(); + AspectGetter = o=>o == null ? "Null" : (o.Text ?? o?.ToString() ?? "Unamed Node"); } } diff --git a/UICatalog/Scenarios/InteractiveTree.cs b/UICatalog/Scenarios/InteractiveTree.cs new file mode 100644 index 000000000..4536b0ad1 --- /dev/null +++ b/UICatalog/Scenarios/InteractiveTree.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terminal.Gui; +using static UICatalog.Scenario; + +namespace UICatalog.Scenarios { + + [ScenarioMetadata (Name: "Interactive Tree", Description: "Create nodes and child nodes in TreeView")] + [ScenarioCategory ("Controls")] + class InteractiveTree : Scenario{ + + TreeView treeView; + + public override void Setup () + { + Win.Title = this.GetName(); + Win.Y = 1; // menu + Win.Height = Dim.Fill (1); // status bar + Top.LayoutSubviews (); + + var menu = new MenuBar (new MenuBarItem [] { + new MenuBarItem ("_File", new MenuItem [] { + new MenuItem ("_Quit", "", () => Quit()), + }) + }); + + treeView = new TreeView() { + X = 0, + Y = 0, + Width = Dim.Fill(), + Height = Dim.Fill(1), + }; + treeView.KeyPress += TreeView_KeyPress; + + Win.Add(treeView); + + var statusBar = new StatusBar (new StatusItem [] { + new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Key.CtrlMask | Key.C, "~^C~ Add Child", () => AddChildNode()), + new StatusItem(Key.CtrlMask | Key.T, "~^T~ Add Root", () => AddRootNode()), + new StatusItem(Key.CtrlMask | Key.R, "~^R~ Rename Node", () => RenameNode()), + }); + Top.Add (statusBar); + + } + + private void TreeView_KeyPress (View.KeyEventEventArgs obj) + { + if(obj.KeyEvent.Key == Key.DeleteChar) { + + var toDelete = treeView.SelectedObject; + + if(toDelete == null) + return; + + obj.Handled = true; + + // if it is a root object remove it + if(treeView.Objects.Contains(toDelete)) { + treeView.Remove(toDelete); + } + else { + var parent = treeView.GetParent(toDelete); + + if(parent == null) + MessageBox.ErrorQuery("Could not delete",$"Parent of '{toDelete}' was unexpectedly null","Ok"); + else { + //update the model + parent.Children.Remove(toDelete); + + //refresh the tree + treeView.RefreshObject(parent); + } + } + } + } + + private void RenameNode () + { + var node = treeView.SelectedObject; + + if(node != null) { + if(GetText("Text","Enter text for node:",node.Text,out string entered)) { + node.Text = entered; + treeView.RefreshObject(node); + } + } + } + + private void AddRootNode () + { + if(GetText("Text","Enter text for node:","",out string entered)) { + treeView.AddObject(new TreeNode(entered)); + } + } + + private void AddChildNode() + { + var node = treeView.SelectedObject; + + if(node != null) { + if(GetText("Text","Enter text for node:","",out string entered)) { + node.Children.Add(new TreeNode(entered)); + treeView.RefreshObject(node); + } + } + } + + private bool GetText(string title, string label, string initialText, out string enteredText) + { + bool okPressed = false; + + var ok = new Button ("Ok", is_default: true); + ok.Clicked += () => { okPressed = true; Application.RequestStop (); }; + var cancel = new Button ("Cancel"); + cancel.Clicked += () => { Application.RequestStop (); }; + var d = new Dialog (title, 60, 20, ok, cancel); + + var lbl = new Label() { + X = 0, + Y = 1, + Text = label + }; + + var tf = new TextField() + { + Text = initialText, + X = 0, + Y = 2, + Width = Dim.Fill() + }; + + d.Add (lbl,tf); + tf.SetFocus(); + + Application.Run (d); + + enteredText = okPressed? tf.Text.ToString() : null; + return okPressed; + } + + private void Quit () + { + Application.RequestStop (); + } + } +} diff --git a/UICatalog/Scenarios/TreeUseCases.cs b/UICatalog/Scenarios/TreeUseCases.cs index 9a5358442..cd09aedc4 100644 --- a/UICatalog/Scenarios/TreeUseCases.cs +++ b/UICatalog/Scenarios/TreeUseCases.cs @@ -43,7 +43,7 @@ namespace UICatalog.Scenarios { } // Your data class - private class House : ITreeNode { + private class House : TreeNode { // Your properties @@ -59,14 +59,10 @@ namespace UICatalog.Scenarios { return Address; } } - private class Room : ITreeNode{ + private class Room : TreeNode{ public string Name {get;set;} - - // Rooms have no sub objects - public IList Children => new List(); - public override string ToString () { return Name; diff --git a/docfx/articles/treeview.md b/docfx/articles/treeview.md index 848a191b8..1000d10ad 100644 --- a/docfx/articles/treeview.md +++ b/docfx/articles/treeview.md @@ -34,7 +34,7 @@ Having to create a bunch of TreeNode objects can be a pain especially if you alr ```csharp // Your data class -private class House : ITreeNode { +private class House : TreeNode { // Your properties @@ -52,7 +52,7 @@ private class House : ITreeNode { } // Your other data class -private class Room : ITreeNode{ +private class Room : TreeNode{ public string Name {get;set;}