浏览代码

Fixes #3918 and #3913 - `Accepting` behavior (#3921)

* Fixed #3905, #3918

* Tweaked Generic

* Label code cleanup

* Clean up.

* Clean up.

* Clean up2.
Tig 5 月之前
父节点
当前提交
7ba6d638bc

+ 6 - 2
CommunityToolkitExample/LoginView.cs

@@ -19,15 +19,19 @@ internal partial class LoginView : IRecipient<Message<LoginActions>>
                                      {
                                      {
                                          ViewModel.Password = passwordInput.Text;
                                          ViewModel.Password = passwordInput.Text;
                                      };
                                      };
-        loginButton.Accepting += (_, _) =>
+        loginButton.Accepting += (_, e) =>
                               {
                               {
                                   if (!ViewModel.CanLogin) { return; }
                                   if (!ViewModel.CanLogin) { return; }
                                   ViewModel.LoginCommand.Execute (null);
                                   ViewModel.LoginCommand.Execute (null);
+                                  // Anytime Accepting is handled, make sure to set e.Cancel to false.
+                                  e.Cancel = false;
                               };
                               };
 
 
-        clearButton.Accepting += (_, _) =>
+        clearButton.Accepting += (_, e) =>
                               {
                               {
                                   ViewModel.ClearCommand.Execute (null);
                                   ViewModel.ClearCommand.Execute (null);
+                                  // Anytime Accepting is handled, make sure to set e.Cancel to false.
+                                  e.Cancel = false;
                               };
                               };
 
 
         Initialized += (_, _) => { ViewModel.Initialized (); };
         Initialized += (_, _) => { ViewModel.Initialized (); };

+ 2 - 0
Example/Example.cs

@@ -78,6 +78,8 @@ public class ExampleWindow : Window
                                {
                                {
                                    MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
                                    MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
                                }
                                }
+                               // Anytime Accepting is handled, make sure to set e.Cancel to false.
+                               e.Cancel = false;
                            };
                            };
 
 
         // Add the views to the Window
         // Add the views to the Window

+ 2 - 0
NativeAot/Program.cs

@@ -105,6 +105,8 @@ public class ExampleWindow : Window
             {
             {
                 MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
                 MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
             }
             }
+            // Anytime Accepting is handled, make sure to set e.Cancel to false.
+            e.Cancel = false;
         };
         };
 
 
         // Add the views to the Window
         // Add the views to the Window

+ 2 - 0
SelfContained/Program.cs

@@ -104,6 +104,8 @@ public class ExampleWindow : Window
                                {
                                {
                                    MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
                                    MessageBox.ErrorQuery ("Logging In", "Incorrect username or password", "Ok");
                                }
                                }
+                               // Anytime Accepting is handled, make sure to set e.Cancel to false.
+                               e.Cancel = false;
                            };
                            };
 
 
         // Add the views to the Window
         // Add the views to the Window

+ 4 - 0
Terminal.Gui/FileServices/DefaultFileOperations.cs

@@ -139,6 +139,8 @@ public class DefaultFileOperations : IFileOperations
                          {
                          {
                              confirm = true;
                              confirm = true;
                              Application.RequestStop ();
                              Application.RequestStop ();
+                             // Anytime Accepting is handled, make sure to set e.Cancel to false.
+                             e.Cancel = false;
                          };
                          };
         var btnCancel = new Button { Text = Strings.btnCancel };
         var btnCancel = new Button { Text = Strings.btnCancel };
 
 
@@ -146,6 +148,8 @@ public class DefaultFileOperations : IFileOperations
                              {
                              {
                                  confirm = false;
                                  confirm = false;
                                  Application.RequestStop ();
                                  Application.RequestStop ();
+                                 // Anytime Accepting is handled, make sure to set e.Cancel to false.
+                                 e.Cancel = false;
                              };
                              };
 
 
         var lbl = new Label { Text = Strings.fdRenamePrompt };
         var lbl = new Label { Text = Strings.fdRenamePrompt };

+ 2 - 1
Terminal.Gui/View/View.Mouse.cs

@@ -559,7 +559,8 @@ public partial class View // Mouse APIs
             // If mouse is still in bounds, generate a click
             // If mouse is still in bounds, generate a click
             if (!WantMousePositionReports && Viewport.Contains (mouseEvent.Position))
             if (!WantMousePositionReports && Viewport.Contains (mouseEvent.Position))
             {
             {
-                return RaiseMouseClickEvent (mouseEvent);
+
+               return RaiseMouseClickEvent (mouseEvent);
             }
             }
 
 
             return mouseEvent.Handled = true;
             return mouseEvent.Handled = true;

+ 3 - 3
Terminal.Gui/Views/Button.cs

@@ -165,10 +165,10 @@ public class Button : View, IDesignable
     ///         If <see langword="true"/>:
     ///         If <see langword="true"/>:
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         - the Button will display an indicator that it is the default Button.
+    ///         - The Button will display an indicator that it is the default Button.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         - when clicked, if the Accepting event is not handled, <see cref="Command.Accept"/> will be
+    ///         - When clicked, if the Accepting event is not handled, <see cref="Command.Accept"/> will be
     ///         invoked on the SuperView.
     ///         invoked on the SuperView.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
@@ -197,7 +197,7 @@ public class Button : View, IDesignable
 
 
     /// <summary>
     /// <summary>
     ///     Gets or sets whether the Button will show decorations or not. If <see langword="true"/> the glyphs that normally
     ///     Gets or sets whether the Button will show decorations or not. If <see langword="true"/> the glyphs that normally
-    ///     brakcet the Button Title and the <see cref="IsDefault"/> indicator will not be shown.
+    ///     bracket the Button Title and the <see cref="IsDefault"/> indicator will not be shown.
     /// </summary>
     /// </summary>
     public bool NoDecorations { get; set; }
     public bool NoDecorations { get; set; }
 
 

+ 9 - 7
Terminal.Gui/Views/Label.cs

@@ -3,7 +3,7 @@
 /// <summary>
 /// <summary>
 ///     The Label <see cref="View"/> displays text that describes the View next in the <see cref="View.Subviews"/>. When
 ///     The Label <see cref="View"/> displays text that describes the View next in the <see cref="View.Subviews"/>. When
 ///     Label
 ///     Label
-///     recieves a <see cref="Command.HotKey"/> command it will pass it to the next <see cref="View"/> in
+///     receives a <see cref="Command.HotKey"/> command it will pass it to the next <see cref="View"/> in
 ///     <see cref="View.Subviews"/>.
 ///     <see cref="View.Subviews"/>.
 /// </summary>
 /// </summary>
 /// <remarks>
 /// <remarks>
@@ -13,7 +13,7 @@
 ///     <para>
 ///     <para>
 ///         If <see cref="View.CanFocus"/> is <see langword="false"/> and the use clicks on the Label,
 ///         If <see cref="View.CanFocus"/> is <see langword="false"/> and the use clicks on the Label,
 ///         the <see cref="Command.HotKey"/> will be invoked on the next <see cref="View"/> in
 ///         the <see cref="Command.HotKey"/> will be invoked on the next <see cref="View"/> in
-///         <see cref="View.Subviews"/>."
+///         <see cref="View.Subviews"/>.
 ///     </para>
 ///     </para>
 /// </remarks>
 /// </remarks>
 public class Label : View, IDesignable
 public class Label : View, IDesignable
@@ -31,7 +31,6 @@ public class Label : View, IDesignable
         MouseClick += Label_MouseClick;
         MouseClick += Label_MouseClick;
     }
     }
 
 
-    // TODO: base raises Select, but we want to raise HotKey. This can be simplified?
     private void Label_MouseClick (object sender, MouseEventArgs e)
     private void Label_MouseClick (object sender, MouseEventArgs e)
     {
     {
         if (!CanFocus)
         if (!CanFocus)
@@ -74,12 +73,15 @@ public class Label : View, IDesignable
             return true;
             return true;
         }
         }
 
 
-        int me = SuperView?.Subviews.IndexOf (this) ?? -1;
-
-        if (me != -1 && me < SuperView?.Subviews.Count - 1)
+        if (HotKey.IsValid)
         {
         {
+            int me = SuperView?.Subviews.IndexOf (this) ?? -1;
+
+            if (me != -1 && me < SuperView?.Subviews.Count - 1)
+            {
 
 
-            return SuperView?.Subviews [me + 1].InvokeCommand (Command.HotKey) == true;
+                return SuperView?.Subviews [me + 1].InvokeCommand (Command.HotKey) == true;
+            }
         }
         }
 
 
         return false;
         return false;

+ 7 - 1
UICatalog/Scenarios/Generic.cs

@@ -20,7 +20,13 @@ public sealed class Generic : Scenario
 
 
         var button = new Button { Id = "button", X = Pos.Center (), Y = 1, Text = "_Press me!" };
         var button = new Button { Id = "button", X = Pos.Center (), Y = 1, Text = "_Press me!" };
 
 
-        button.Accepting += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "_Ok");
+        button.Accepting += (s, e) =>
+                            {
+                                // Anytime Accepting is handled, make sure to set e.Cancel to false.
+                                e.Cancel = true;
+                                MessageBox.ErrorQuery ("Error", "You pressed the button!", "_Ok");
+                            };
+
         appWindow.Add (button);
         appWindow.Add (button);
 
 
         // Run - Start the application.
         // Run - Start the application.

+ 144 - 2
UnitTests/View/ViewCommandTests.cs

@@ -10,7 +10,7 @@ public class ViewCommandTests
         var view = new ViewEventTester ();
         var view = new ViewEventTester ();
         Assert.False (view.HasFocus);
         Assert.False (view.HasFocus);
 
 
-        Assert.False (view.InvokeCommand (Command.Accept)); // false means it was not handled
+        Assert.False (view.InvokeCommand (Command.Accept)); // there's no superview, so it should return true?
 
 
         Assert.Equal (1, view.OnAcceptedCount);
         Assert.Equal (1, view.OnAcceptedCount);
 
 
@@ -124,6 +124,148 @@ public class ViewCommandTests
         Assert.Equal (0, view.OnAcceptedCount);
         Assert.Equal (0, view.OnAcceptedCount);
     }
     }
 
 
+    // See https://github.com/gui-cs/Terminal.Gui/issues/3913
+    [Fact]
+    public void Button_IsDefault_Raises_Accepted_Correctly ()
+    {
+        int A_AcceptedCount = 0;
+        bool A_CancelAccepting = false;
+
+        int B_AcceptedCount = 0;
+        bool B_CancelAccepting = false;
+
+        var w = new Window ()
+        {
+            BorderStyle = LineStyle.None,
+            Width = 10,
+            Height = 10
+        };
+
+        var btnA = new Button ()
+        {
+            Width = 3,
+            IsDefault = true
+        };
+        btnA.Accepting += (s, e) =>
+                          {
+                              A_AcceptedCount++;
+                              e.Cancel = A_CancelAccepting;
+                          };
+
+        var btnB = new Button ()
+        {
+            Width = 3,
+            X = Pos.Right (btnA)
+        };
+
+        btnB.Accepting += (s, e) =>
+                          {
+                              B_AcceptedCount++;
+                              e.Cancel = B_CancelAccepting;
+                          };
+        w.Add (btnA, btnB);
+
+        w.LayoutSubviews ();
+
+        Application.Begin (w);
+        Assert.Same (Application.Top, w);
+
+        // Click button 2
+        var btn2Frame = btnB.FrameToScreen ();
+
+        Application.RaiseMouseEvent (
+                         new MouseEventArgs ()
+                         {
+                             ScreenPosition = btn2Frame.Location,
+                             Flags = MouseFlags.Button1Clicked
+                         });
+
+        // Button A should have been accepted because B didn't cancel and A IsDefault
+        Assert.Equal (1, A_AcceptedCount);
+        Assert.Equal (1, B_AcceptedCount);
+
+        B_CancelAccepting = true;
+        Application.RaiseMouseEvent (
+                                     new MouseEventArgs ()
+                                     {
+                                         ScreenPosition = btn2Frame.Location,
+                                         Flags = MouseFlags.Button1Clicked
+                                     });
+
+        // Button A (IsDefault) should NOT have been accepted because B canceled
+        Assert.Equal (1, A_AcceptedCount);
+        Assert.Equal (2, B_AcceptedCount);
+    }
+
+    // See: https://github.com/gui-cs/Terminal.Gui/issues/3905
+    [Fact]
+    public void Button_CanFocus_False_Raises_Accepted_Correctly ()
+    {
+        int wAcceptedCount = 0;
+        bool wCancelAccepting = false;
+        var w = new Window ()
+        {
+            Title = "Window",
+            BorderStyle = LineStyle.None,
+            Width = 10,
+            Height = 10
+        };
+
+        w.Accepting += (s, e) =>
+                       {
+                           wAcceptedCount++;
+                           e.Cancel = wCancelAccepting;
+                       };
+
+        int btnAcceptedCount = 0;
+        bool btnCancelAccepting = false;
+        var btn = new Button ()
+        {
+            Title = "Button",
+            Width = 3,
+            IsDefault = true,
+        };
+        btn.CanFocus = true;
+
+        btn.Accepting += (s, e) =>
+                         {
+                             btnAcceptedCount++;
+                             e.Cancel = btnCancelAccepting;
+                         };
+
+        w.Add (btn);
+
+        w.LayoutSubviews ();
+
+        Application.Begin (w);
+
+        // Click button just like a driver would
+        var btnFrame = btn.FrameToScreen ();
+        Application.RaiseMouseEvent (
+                                     new MouseEventArgs ()
+                                     {
+                                         ScreenPosition = btnFrame.Location,
+                                         Flags = MouseFlags.Button1Pressed
+                                     });
+
+        Application.RaiseMouseEvent (
+                                     new MouseEventArgs ()
+                                     {
+                                         ScreenPosition = btnFrame.Location,
+                                         Flags = MouseFlags.Button1Released
+                                     });
+
+        Application.RaiseMouseEvent (
+                                     new MouseEventArgs ()
+                                     {
+                                         ScreenPosition = btnFrame.Location,
+                                         Flags = MouseFlags.Button1Clicked
+                                     });
+
+        Assert.Equal (1, btnAcceptedCount);
+        Assert.Equal (2, wAcceptedCount);
+    }
+
     #endregion OnAccept/Accept tests
     #endregion OnAccept/Accept tests
 
 
     #region OnSelect/Select tests
     #region OnSelect/Select tests
@@ -140,7 +282,7 @@ public class ViewCommandTests
         Assert.Equal (canFocus, view.CanFocus);
         Assert.Equal (canFocus, view.CanFocus);
         Assert.False (view.HasFocus);
         Assert.False (view.HasFocus);
 
 
-        Assert.Equal (canFocus, view.InvokeCommand (Command.Select));
+        view.InvokeCommand (Command.Select);
 
 
         Assert.Equal (1, view.OnSelectingCount);
         Assert.Equal (1, view.OnSelectingCount);
 
 

+ 13 - 7
UnitTests/Views/LabelTests.cs

@@ -1,4 +1,5 @@
 using System.ComponentModel;
 using System.ComponentModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
 using Xunit.Abstractions;
 using Xunit.Abstractions;
 
 
 namespace Terminal.Gui.ViewsTests;
 namespace Terminal.Gui.ViewsTests;
@@ -30,11 +31,13 @@ public class LabelTests (ITestOutputHelper output)
         Assert.Equal ("Hello", label.TitleTextFormatter.Text);
         Assert.Equal ("Hello", label.TitleTextFormatter.Text);
     }
     }
 
 
-    [Fact]
-    public void HotKey_Command_SetsFocus_OnNextSubview ()
+    [Theory]
+    [CombinatorialData]
+    public void HotKey_Command_SetsFocus_OnNextSubview (bool hasHotKey)
     {
     {
         var superView = new View { CanFocus = true };
         var superView = new View { CanFocus = true };
         var label = new Label ();
         var label = new Label ();
+        label.HotKey = hasHotKey ? Key.A.WithAlt : Key.Empty;
         var nextSubview = new View { CanFocus = true };
         var nextSubview = new View { CanFocus = true };
         superView.Add (label, nextSubview);
         superView.Add (label, nextSubview);
         superView.BeginInit ();
         superView.BeginInit ();
@@ -45,15 +48,18 @@ public class LabelTests (ITestOutputHelper output)
 
 
         label.InvokeCommand (Command.HotKey);
         label.InvokeCommand (Command.HotKey);
         Assert.False (label.HasFocus);
         Assert.False (label.HasFocus);
-        Assert.True (nextSubview.HasFocus);
+        Assert.Equal (hasHotKey, nextSubview.HasFocus);
     }
     }
 
 
-    [Fact]
-    public void MouseClick_SetsFocus_OnNextSubview ()
+    [Theory]
+    [CombinatorialData]
+    public void MouseClick_SetsFocus_OnNextSubview (bool hasHotKey)
     {
     {
         var superView = new View { CanFocus = true, Height = 1, Width = 15 };
         var superView = new View { CanFocus = true, Height = 1, Width = 15 };
         var focusedView = new View { CanFocus = true, Width = 1, Height = 1 };
         var focusedView = new View { CanFocus = true, Width = 1, Height = 1 };
-        var label = new Label { X = 2, Title = "_x" };
+        var label = new Label { X = 2 };
+        label.HotKey = hasHotKey ? Key.X.WithAlt : Key.Empty;
+
         var nextSubview = new View { CanFocus = true, X = 4, Width = 4, Height = 1 };
         var nextSubview = new View { CanFocus = true, X = 4, Width = 4, Height = 1 };
         superView.Add (focusedView, label, nextSubview);
         superView.Add (focusedView, label, nextSubview);
         superView.BeginInit ();
         superView.BeginInit ();
@@ -65,7 +71,7 @@ public class LabelTests (ITestOutputHelper output)
 
 
         label.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked });
         label.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked });
         Assert.False (label.HasFocus);
         Assert.False (label.HasFocus);
-        Assert.True (nextSubview.HasFocus);
+        Assert.Equal (hasHotKey, nextSubview.HasFocus);
     }
     }
 
 
     [Fact]
     [Fact]

二进制
local_packages/Terminal.Gui.2.0.0.nupkg


二进制
local_packages/Terminal.Gui.2.0.0.snupkg