Browse Source

Added new method HitTest to TreeView to allow for better context menus

Thomas Nind 3 years ago
parent
commit
5c39f6af48

+ 15 - 0
Terminal.Gui/Views/TreeView.cs

@@ -671,6 +671,21 @@ namespace Terminal.Gui {
 			ObjectActivated?.Invoke (e);
 		}
 
+		/// <summary>
+		/// Returns the object in the tree list that is currently visible
+		/// at the provided point.  Returns null if no object is at that location.
+		/// <remarks>
+		/// </remarks>
+		/// If you have screen cordinates then use <see cref="View.ScreenToView(int, int)"/>
+		/// to translate these into the client area of the <see cref="TreeView{T}"/>.
+		/// </summary>
+		/// <param name="point">Point with the <see cref="View.Bounds"/> of the <see cref="TreeView{T}"/></param>
+		/// <returns></returns>
+		public T HitTest (Point point)
+		{
+			return HitTest (point.Y)?.Model;
+		}
+
 		///<inheritdoc/>
 		public override bool MouseEvent (MouseEvent me)
 		{

+ 47 - 0
UICatalog/Scenarios/TreeViewFileSystem.cs

@@ -77,6 +77,7 @@ namespace UICatalog.Scenarios {
 			};
 
 			treeViewFiles.ObjectActivated += TreeViewFiles_ObjectActivated;
+			treeViewFiles.MouseClick += TreeViewFiles_MouseClick;
 
 			SetupFileTree ();
 
@@ -88,6 +89,52 @@ namespace UICatalog.Scenarios {
 			red = Application.Driver.MakeAttribute (Color.Red, Color.Blue);
 		}
 
+		private void TreeViewFiles_MouseClick (View.MouseEventArgs obj)
+		{
+			// if user right clicks
+			if (obj.MouseEvent.Flags.HasFlag(MouseFlags.Button3Clicked)) {
+
+				var rightClicked = treeViewFiles.HitTest (new Point (obj.MouseEvent.X, obj.MouseEvent.Y));
+
+				// nothing was clicked
+				if (rightClicked == null)
+					return;
+
+				var menu = new ContextMenu ();
+				menu.Position = new Point(
+					obj.MouseEvent.X + treeViewFiles.Frame.X,
+					obj.MouseEvent.Y + treeViewFiles.Frame.Y +1);
+
+				menu.MenuItems = new MenuBarItem (new [] { new MenuItem ("Properties",null,()=> {
+					MessageBox.Query($"{rightClicked.Name}({rightClicked.GetType().Name})",Describe(rightClicked),"Ok");
+				}
+					
+				) });
+				menu.Show ();
+
+			}
+		}
+
+		private string Describe (FileSystemInfo f)
+		{
+			try {
+
+				if (f is FileInfo fi) {
+					return "Size:" + fi.Length;
+				}
+
+				if (f is DirectoryInfo d) {
+					return $@"Parent:{d.Parent}
+Attributes:{d.Attributes}";
+				}
+			} catch (Exception) {
+
+				return "Could not get properties";
+			}
+
+			return null;
+		}
+
 		private void SetupScrollBar ()
 		{
 			// When using scroll bar leave the last row of the control free (for over-rendering with scroll bar)

+ 48 - 1
UnitTests/TreeViewTests.cs

@@ -720,8 +720,55 @@ namespace Terminal.Gui.Views {
 			Assert.Equal (1, tree.GetChildren (root).Count (child => ReferenceEquals (obj2, child)));
 
 		}
-
 		[Fact, AutoInitShutdown]
+		public void TestTreeHitTest ()
+		{
+			var tv = new TreeView { Width = 20, Height = 10 };
+
+			var n1 = new TreeNode ("normal");
+			var n1_1 = new TreeNode ("pink");
+			var n1_2 = new TreeNode ("normal");
+			n1.Children.Add (n1_1);
+			n1.Children.Add (n1_2);
+
+			var n2 = new TreeNode ("pink");
+			tv.AddObject (n1);
+			tv.AddObject (n2);
+			tv.Expand (n1);
+
+			tv.ColorScheme = new ColorScheme ();
+			tv.Redraw (tv.Bounds);
+
+			GraphViewTests.AssertDriverContentsAre (
+@"├-normal
+│ ├─pink
+│ └─normal
+└─pink
+", output);
+
+			Assert.Same (n1, tv.HitTest (new Point (0, 0)));
+			Assert.Same (n1_1, tv.HitTest (new Point (0, 1)));
+			Assert.Same (n1_2, tv.HitTest (new Point (0, 2)));
+			Assert.Same (n2, tv.HitTest (new Point (0, 3)));
+			Assert.Null (tv.HitTest (new Point (0, 4)));
+
+			tv.Collapse (n1);
+
+			tv.Redraw (tv.Bounds);
+
+
+			GraphViewTests.AssertDriverContentsAre (
+@"├+normal
+└─pink
+", output);
+
+			Assert.Same (n1, tv.HitTest (new Point (0, 0)));
+			Assert.Same (n2, tv.HitTest (new Point (0, 1)));
+			Assert.Null (tv.HitTest (new Point (0, 2)));
+			Assert.Null (tv.HitTest (new Point (0, 3)));
+			Assert.Null (tv.HitTest (new Point (0, 4)));
+		}
+			[Fact, AutoInitShutdown]
 		public void TestTreeViewColor()
 		{
 			var tv = new TreeView{Width = 20,Height = 10};