Forráskód Böngészése

Merge pull request #2100 from tznind/cancel-mouse-nonbreaking

Fixes #2096. Makes RootMouseEvent cancellable
Tig 2 éve
szülő
commit
a823b622ab

+ 5 - 0
Terminal.Gui/Core/Application.cs

@@ -670,6 +670,11 @@ namespace Terminal.Gui {
 				me.View = view;
 			}
 			RootMouseEvent?.Invoke (me);
+
+			if (me.Handled) {
+				return;
+			}
+
 			if (mouseGrabView != null) {
 				if (view == null) {
 					UngrabMouse ();

+ 18 - 8
Terminal.Gui/Core/Event.cs

@@ -706,38 +706,48 @@ namespace Terminal.Gui {
 	}
 
 	/// <summary>
-	/// Describes a mouse event
+	/// Low-level construct that conveys the details of mouse events, such
+	/// as coordinates and button state, from ConsoleDrivers up to <see cref="Application"/> and
+	/// Views.
 	/// </summary>
-	public struct MouseEvent {
+	/// <remarks>The <see cref="Application"/> class includes the <see cref="Application.RootMouseEvent"/>
+	/// Action which takes a MouseEvent argument.</remarks>
+	public class MouseEvent {
 		/// <summary>
 		/// The X (column) location for the mouse event.
 		/// </summary>
-		public int X;
+		public int X { get; set; }
 
 		/// <summary>
 		/// The Y (column) location for the mouse event.
 		/// </summary>
-		public int Y;
+		public int Y { get; set; }
 
 		/// <summary>
 		/// Flags indicating the kind of mouse event that is being posted.
 		/// </summary>
-		public MouseFlags Flags;
+		public MouseFlags Flags { get; set; }
 
 		/// <summary>
 		/// The offset X (column) location for the mouse event.
 		/// </summary>
-		public int OfX;
+		public int OfX { get; set; }
 
 		/// <summary>
 		/// The offset Y (column) location for the mouse event.
 		/// </summary>
-		public int OfY;
+		public int OfY { get; set; }
 
 		/// <summary>
 		/// The current view at the location for the mouse event.
 		/// </summary>
-		public View View;
+		public View View { get; set; }
+
+		/// <summary>
+		/// Indicates if the current mouse event has already been processed and the driver should stop notifying any other event subscriber.
+		/// Its important to set this value to true specially when updating any View's layout from inside the subscriber method.
+		/// </summary>
+		public bool Handled { get; set; }
 
 		/// <summary>
 		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="MouseEvent"/>.

+ 10 - 2
Terminal.Gui/Core/View.cs

@@ -2743,7 +2743,9 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Specifies the event arguments for <see cref="MouseEvent"/>
+		/// Specifies the event arguments for <see cref="MouseEvent"/>.  This is a higher-level construct
+		/// than the wrapped <see cref="MouseEvent"/> class and is used for the events defined on <see cref="View"/>
+		/// and subclasses of View (e.g. <see cref="View.MouseEnter"/> and <see cref="View.MouseClick"/>).
 		/// </summary>
 		public class MouseEventArgs : EventArgs {
 			/// <summary>
@@ -2755,11 +2757,17 @@ namespace Terminal.Gui {
 			/// The <see cref="MouseEvent"/> for the event.
 			/// </summary>
 			public MouseEvent MouseEvent { get; set; }
+			
 			/// <summary>
 			/// Indicates if the current mouse event has already been processed and the driver should stop notifying any other event subscriber.
 			/// Its important to set this value to true specially when updating any View's layout from inside the subscriber method.
 			/// </summary>
-			public bool Handled { get; set; }
+			/// <remarks>This property forwards to the <see cref="MouseEvent.Handled"/> property and is provided as a convenience and for
+			/// backwards compatibility</remarks>
+			public bool Handled { 
+				get => MouseEvent.Handled;
+				set => MouseEvent.Handled = value;
+			}
 		}
 
 		/// <inheritdoc/>

+ 52 - 0
UnitTests/TextFieldTests.cs

@@ -1203,6 +1203,52 @@ namespace Terminal.Gui.Views {
 			Application.Driver.SendKeys ('j', ConsoleKey.A, false, false, false);
 			Assert.Equal ("aj", tf.Text.ToString ());
 		}
+		[Fact]
+		[AutoInitShutdown]
+		public void Test_RootMouseKeyEvent_Cancel ()
+		{
+			Application.RootMouseEvent += SuppressRightClick;
+
+			var tf = new TextField () { Width = 10 };
+			int clickCounter = 0;
+			tf.MouseClick += (m) => { clickCounter++; };
+
+			Application.Top.Add (tf);
+			Application.Begin (Application.Top);
+
+			var processMouseEventMethod = typeof (Application).GetMethod ("ProcessMouseEvent", BindingFlags.Static | BindingFlags.NonPublic)
+				?? throw new Exception ("Expected private method not found 'ProcessMouseEvent', this method was used for testing mouse behaviours");
+
+			var mouseEvent = new MouseEvent {
+				Flags = MouseFlags.Button1Clicked,
+				View = tf
+			};
+
+			processMouseEventMethod.Invoke (null, new object [] { mouseEvent });
+			Assert.Equal (1, clickCounter);
+
+			// Get a fresh instance that represents a right click.
+			// Should be ignored because of SuppressRightClick callback
+			mouseEvent = new MouseEvent {
+				Flags = MouseFlags.Button3Clicked,
+				View = tf
+			};
+			processMouseEventMethod.Invoke (null, new object [] { mouseEvent });
+			Assert.Equal (1, clickCounter);
+
+			Application.RootMouseEvent -= SuppressRightClick;
+
+			// Get a fresh instance that represents a right click.
+			// Should no longer be ignored as the callback was removed
+			mouseEvent = new MouseEvent {
+				Flags = MouseFlags.Button3Clicked,
+				View = tf
+			};
+
+			processMouseEventMethod.Invoke (null, new object [] { mouseEvent });
+			Assert.Equal (2, clickCounter);
+		}
+
 
 		private bool SuppressKey (KeyEvent arg)
 		{
@@ -1212,6 +1258,12 @@ namespace Terminal.Gui.Views {
 			return false;
 		}
 
+		private void SuppressRightClick (MouseEvent arg)
+		{
+			if (arg.Flags.HasFlag (MouseFlags.Button3Clicked))
+				arg.Handled = true;
+		}
+
 		[Fact, AutoInitShutdown]
 		public void ScrollOffset_Initialize ()
 		{