Tig 11 місяців тому
батько
коміт
667a8ee152

+ 7 - 2
Terminal.Gui/View/View.Navigation.cs

@@ -265,7 +265,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
                 return;
             }
 
-            if (Application.Navigation is { } && Application.Current is {})
+            if (Application.Navigation is { } && Application.Current is { })
             {
                 // Temporarily ensure this view can't get focus
                 bool prevCanFocus = _canFocus;
@@ -312,6 +312,12 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
         NotifyFocusChanged (HasFocus, this, newFocusedVew);
 
+        if (_hasFocus)
+        {
+            // Notify caused HasFocus to change to true.
+            return;
+        }
+
         if (SuperView is { })
         {
             SuperView._previouslyMostFocused = focusedPeer;
@@ -452,7 +458,6 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
         // The subview does not have focus, but at least one other that can. Can this one be focused?
         (bool focusSet, bool _) = view.SetHasFocusTrue (Focused);
-
         return focusSet;
     }
 

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

@@ -377,7 +377,8 @@ public partial class View : Responder, ISupportInitializeNotification
                 }
             }
 
-            if (_visible && CanFocus && Enabled && !HasFocus && SuperView?.Focused == null)
+            if (_visible && CanFocus && Enabled && !HasFocus 
+                && (SuperView is null || SuperView is { HasFocus: true, Visible: true, Enabled: true, Focused: null }))
             {
                 SetFocus ();
             }

+ 72 - 0
UnitTests/View/Navigation/CanFocusTests.cs

@@ -287,7 +287,79 @@ public class CanFocusTests (ITestOutputHelper _output) : TestsAllViews
         r.Dispose ();
     }
 
+    [Fact]
+    public void CanFocus_True_Focuses ()
+    {
+        View view = new ()
+        {
+            Id = "view"
+        };
+
+        View superView = new ()
+        {
+            Id = "superView",
+            CanFocus = true
+        };
+
+        superView.Add (view);
+
+        superView.SetFocus ();
+        Assert.True (superView.HasFocus);
+        Assert.NotEqual (view, superView.Focused);
+
+        view.CanFocus = true;
+        Assert.True (superView.HasFocus);
+        Assert.Equal (view, superView.Focused);
+        Assert.True (view.HasFocus);
+
+        view.CanFocus = false;
+        Assert.True (superView.HasFocus);
+        Assert.NotEqual (view, superView.Focused);
+        Assert.False (view.HasFocus);
+    }
+
 
+    [Fact]
+    public void CanFocus_Set_True_Get_AdvanceFocus_Works ()
+    {
+        Label label = new () { Text = "label" };
+        View view = new () { Text = "view", CanFocus = true };
+        Application.Current = new ();
+        Application.Current.Add (label, view);
+
+        Application.Current.SetFocus ();
+        Assert.Equal (view, Application.Current.MostFocused);
+        Assert.False (label.CanFocus);
+        Assert.False (label.HasFocus);
+        Assert.True (view.CanFocus);
+        Assert.True (view.HasFocus);
+
+        Assert.False (Application.Current.AdvanceFocus (NavigationDirection.Forward, null));
+        Assert.False (label.HasFocus);
+        Assert.True (view.HasFocus);
+
+        // Set label CanFocus to true
+        label.CanFocus = true;
+        Assert.False (label.HasFocus);
+        Assert.True (view.HasFocus);
+
+        // label can now be focused, so AdvanceFocus should move to it.
+        Assert.True (Application.Current.AdvanceFocus (NavigationDirection.Forward, null));
+        Assert.True (label.HasFocus);
+        Assert.False (view.HasFocus);
+
+        // Move back to view
+        view.SetFocus ();
+        Assert.False (label.HasFocus);
+        Assert.True (view.HasFocus);
+
+        Assert.True (Application.OnKeyDown (Key.Tab));
+        Assert.True (label.HasFocus);
+        Assert.False (view.HasFocus);
+
+        Application.Current.Dispose ();
+        Application.ResetState ();
+    }
 
 #if V2_NEW_FOCUS_IMPL // Bogus test - depends on auto CanFocus behavior
 

+ 61 - 0
UnitTests/View/Navigation/HasFocusChangeEventTests.cs

@@ -945,5 +945,66 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
         Assert.Equal (0, subviewSubView3EnterCount);
         Assert.Equal (0, subviewSubView3LeaveCount);
     }
+
+
+    [Fact]
+    public void HasFocusChanged_NewValue_False_Hide_Subview ()
+    {
+        var subView1 = new View
+        {
+            Id = $"subView1",
+            CanFocus = true
+        };
+
+        var subView2 = new View
+        {
+            Id = $"subView2",
+            CanFocus = true
+        };
+
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        view.HasFocusChanged += (s, e) =>
+        {
+            if (e.NewValue)
+            {
+                Assert.True (view.HasFocus);
+                Assert.True (subView1.HasFocus);
+                Assert.False (subView2.HasFocus);
+
+                subView1.Visible = true;
+                subView2.Visible = false;
+
+                Assert.True (view.HasFocus);
+                Assert.True (subView1.HasFocus);
+                Assert.False (subView2.HasFocus);
+
+            }
+            else
+            {
+                Assert.False (view.HasFocus);
+                Assert.False (subView1.HasFocus);
+                Assert.False (subView2.HasFocus);
+
+                subView1.Visible = false;
+                subView2.Visible = true;
+            }
+        };
+
+        view.Add (subView1, subView2);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView1.HasFocus);
+        Assert.False (subView2.HasFocus);
+
+        view.HasFocus = false;
+        Assert.False (view.HasFocus);
+        Assert.False (subView1.HasFocus);
+        Assert.False (subView2.HasFocus);
+    }
     #endregion HasFocusChanged
 }

+ 66 - 16
UnitTests/Views/LabelTests.cs

@@ -63,7 +63,7 @@ public class LabelTests (ITestOutputHelper output)
         Assert.False (label.HasFocus);
         Assert.False (nextSubview.HasFocus);
 
-        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.True (nextSubview.HasFocus);
     }
@@ -164,7 +164,7 @@ public class LabelTests (ITestOutputHelper output)
         TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
         top.Dispose ();
     }
-    
+
     [Fact]
     public void Constructors_Defaults ()
     {
@@ -1317,33 +1317,31 @@ e
     }
 
     [Fact]
-    [AutoInitShutdown]
-    public void Label_CanFocus_True_Get_Focus_By_Keyboard_And_Mouse ()
+    public void Label_CanFocus_True_Get_Focus_By_Keyboard ()
     {
         Label label = new () { Text = "label" };
-        View view = new () { Y = 2, Width = 10, Height = 1, Text = "view", CanFocus = true };
-        Toplevel top = new ();
-        top.Add (label, view);
-        Application.Begin (top);
+        View view = new () { Text = "view", CanFocus = true };
+        Application.Current = new ();
+        Application.Current.Add (label, view);
 
-        Assert.Equal (new (0, 0, 5, 1), label.Frame);
-        Assert.Equal (new (0, 2, 10, 1), view.Frame);
-        Assert.Equal (view, top.MostFocused);
+        Application.Current.SetFocus ();
+        Assert.Equal (view, Application.Current.MostFocused);
         Assert.False (label.CanFocus);
         Assert.False (label.HasFocus);
         Assert.True (view.CanFocus);
         Assert.True (view.HasFocus);
 
-        Assert.True (Application.OnKeyDown (Key.Tab));
+        // No focused view accepts Tab, and there's no other view to focus, so OnKeyDown returns false
+        Assert.False (Application.OnKeyDown (Key.Tab));
         Assert.False (label.HasFocus);
         Assert.True (view.HasFocus);
 
-        Application.OnMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked });
+        // Set label CanFocus to true
+        label.CanFocus = true;
         Assert.False (label.HasFocus);
         Assert.True (view.HasFocus);
 
-        // Set label CanFocus to true
-        label.CanFocus = true;
+        // No focused view accepts Tab, but label can now be focused, so focus should move to it.
         Assert.True (Application.OnKeyDown (Key.Tab));
         Assert.True (label.HasFocus);
         Assert.False (view.HasFocus);
@@ -1352,12 +1350,64 @@ e
         Assert.False (label.HasFocus);
         Assert.True (view.HasFocus);
 
+        Application.Current.Dispose ();
+        Application.ResetState ();
+    }
+
+
+    [Fact]
+    public void Label_CanFocus_True_Get_Focus_By_Mouse ()
+    {
+        Label label = new ()
+        {
+            Text = "label",
+            X = 0,
+            Y = 0
+        };
+        View view = new ()
+        {
+            Text = "view",
+            X = 0,
+            Y = 1,
+            Width = 4,
+            Height = 1,
+            CanFocus = true
+        };
+        Application.Current = new ()
+        {
+            Width = 10,
+            Height = 10
+        };
+        Application.Current.Add (label, view);
+
+        Application.Current.SetFocus ();
+        Assert.Equal (view, Application.Current.MostFocused);
+        Assert.False (label.CanFocus);
+        Assert.False (label.HasFocus);
+        Assert.True (view.CanFocus);
+        Assert.True (view.HasFocus);
+
+        // label can't focus so clicking on it has no effect
+        Application.OnMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked });
+        Assert.False (label.HasFocus);
+        Assert.True (view.HasFocus);
+
+        // Set label CanFocus to true
+        label.CanFocus = true;
+        Assert.False (label.HasFocus);
+        Assert.True (view.HasFocus);
+
+        // label can focus, so clicking on it set focus
         Application.OnMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked });
         Assert.True (label.HasFocus);
         Assert.False (view.HasFocus);
 
-        Application.OnMouseEvent (new () { Position = new (0, 2), Flags = MouseFlags.Button1Clicked });
+        // click on view
+        Application.OnMouseEvent (new () { Position = new (0, 1), Flags = MouseFlags.Button1Clicked });
         Assert.False (label.HasFocus);
         Assert.True (view.HasFocus);
+
+        Application.Current.Dispose ();
+        Application.ResetState ();
     }
 }