瀏覽代碼

Merged and fixed issues.
Progess on thinking through new design, but not working yet.

Tig 11 月之前
父節點
當前提交
aa4f5228e6

+ 60 - 27
Terminal.Gui/View/View.Navigation.cs

@@ -69,7 +69,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
             {
                 Debug.Assert (view is null || SuperView is null || ApplicationNavigation.IsInHierarchy (SuperView, view));
 
-                if (OnEnter (view))
+                if (EnterFocus (view))
                 {
                     // The change happened
                     // HasFocus is now true
@@ -82,7 +82,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
             }
             else
             {
-                if (OnLeave (view))
+                if (LeaveFocus (view))
                 {
                     // The change happened
                     // HasFocus is now false
@@ -163,31 +163,57 @@ public partial class View // Focus and cross-view navigation management (TabStop
             return false;
         }
 
+        // If we're here, we can be focused. But we may have subviews.
+
+        // Restore focus to the most focused subview in the subview-hierarchy
+        if (!RestoreFocus (TabStop))
+        {
+            // Couldn't restore focus, so use Advance to navigate to the next focusable subview
+            if (!AdvanceFocus (NavigationDirection.Forward, TabStop))
+            {
+                // Couldn't advance, so we're the most-focusable view in the application
+                // We now need to ensure all views up the superview hierarchy have focus.
+                // Any of them may cancel gaining focus. In which case we need to back out.
+
+                // All views down the superview-hierarchy need to have HasFocus == true
+
+                // PLACEHOLDER:But just using SetHasFocus on them will infinitely recurse. So we need to do it manually.
+
+                
+
+
+                if (SuperView is { HasFocus: false } super)
+                {
+                    if (!super.SetHasFocus (true, this))
+                    {
+                        // The change was cancelled
+                        return false;
+                    }
+
+                    SuperView.Focused = this;
+                }
+
+                Application.Navigation?.SetFocused (this);
+            }
+        }
+
+
+
         // Now, these things need to be true for us to set _hasFocus to true:
         //   All views down v's view-hierarchy need to gain focus, if they don't already have it.
         //       Any of them may cancel gaining focus, so we need to do that first.
 
         _hasFocus = true;
 
-        // By setting _hasFocus to true we've definitively changed HasFocus for this view
+        // By setting _hasFocus to true we've definitively changed HasFocus for this view.
+        // But we can back out below if a subview or superview cancels gaining focus.
 
+        // All views down the superview-hierarchy need to have HasFocus == true
         if (SuperView is { })
         {
             SuperView.Focused = this;
         }
 
-        if (!RestoreFocus (TabStop))
-        {
-            // Couldn't restore focus, so use Advance to navigate
-            if (!AdvanceFocus (NavigationDirection.Forward, TabStop))
-            {
-                // Couldn't advance, so we're the most-focusable view in the application
-                ApplicationNavigation.SetFocused (this);
-            }
-        }
-
-        // All views down the superview-hierarchy need to have HasFocus == true
-
         // Post-conditions - prove correctness
         if (HasFocus == previousValue)
         {
@@ -210,6 +236,8 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// </remarks>
     public event EventHandler<FocusEventArgs> Enter;
 
+    // TODO: Leave does not need to be cancelable. That's overkill. Just make it so that it can't be canceled.
+
     /// <summary>
     ///     Called when view is entering focus. This method is called by <see cref="SetHasFocus"/> and other methods that
     ///     set or remove focus from a view.
@@ -227,10 +255,10 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
         // Before we can leave focus, we need to make sure that all views down the subview-hierarchy have left focus.
         //    Any of them may cancel losing focus, so we need to do that first.
-        if (ApplicationNavigation.GetFocused () != this)
+        if (Application.Navigation?.GetFocused () != this)
         {
             // Save the most focused view in the subview-hierarchy
-            View originalBottom = ApplicationNavigation.GetFocused ();
+            View originalBottom = Application.Navigation?.GetFocused ();
             // Start at the bottom and work our way up to us
             View bottom = originalBottom;
 
@@ -273,7 +301,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
         _hasFocus = false;
 
-        if (ApplicationNavigation.GetFocused () != this)
+        if (Application.Navigation?.GetFocused () != this)
         {
             PreviouslyMostFocused = null;
 
@@ -450,26 +478,27 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
     /// <summary>
     ///     Internal API that causes <paramref name="viewToEnterFocus"/> to enter focus.
-    ///     <paramref name="viewToEnterFocus"/> does not need to be a subview.
-    ///     Recursively sets focus DOWN in the view hierarchy.
+    ///     <paramref name="viewToEnterFocus"/> must be a subview.
+    ///     Recursively sets focus up the superview hierarchy.
     /// </summary>
     /// <param name="viewToEnterFocus"></param>
-    private void SetFocus (View viewToEnterFocus)
+    /// <returns><see langword="true"/> if <paramref name="viewToEnterFocus"/> got focus.</returns>
+    private bool SetFocus (View viewToEnterFocus)
     {
         if (viewToEnterFocus is null)
         {
-            return;
+            return false;
         }
 
         if (!viewToEnterFocus.CanFocus || !viewToEnterFocus.Visible || !viewToEnterFocus.Enabled)
         {
-            return;
+            return false;
         }
 
         // If viewToEnterFocus is already the focused view, don't do anything
         if (Focused?._hasFocus == true && Focused == viewToEnterFocus)
         {
-            return;
+            return false;
         }
 
         // If a subview has focus and viewToEnterFocus is the focused view's superview OR viewToEnterFocus is this view,
@@ -481,7 +510,8 @@ public partial class View // Focus and cross-view navigation management (TabStop
                 viewToEnterFocus._hasFocus = true;
             }
 
-            return;
+            // viewToEnterFocus is already focused
+            return true;
         }
 
         // Make sure that viewToEnterFocus is a subview of this view
@@ -509,13 +539,15 @@ public partial class View // Focus and cross-view navigation management (TabStop
         Focused?.SetHasFocus (true, f, true);
         Focused?.FocusDeepest (null, NavigationDirection.Forward);
 
-        // Recursively set focus down the view hierarchy
+        // Recursively set focus up the superview hierarchy
         if (SuperView is { })
         {
+            // BUGBUG: If focus is cancelled at any point, we should stop and restore focus to the previous focused view
             SuperView.SetFocus (this);
         }
         else
         {
+            // BUGBUG: this makes no sense in the new design
             // If there is no SuperView, then this is a top-level view
             SetFocus (this);
 
@@ -533,6 +565,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
             viewToEnterFocus.TabIndex = 0;
         }
 
+        return true;
     }
 
 
@@ -781,7 +814,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     ///     Raises the <see cref="CanFocusChanged"/> event.
     /// </remarks>
     public virtual void OnCanFocusChanged () { CanFocusChanged?.Invoke (this, EventArgs.Empty); }
-    
+
     #region Tab/Focus Handling
 
 #nullable enable

+ 8 - 8
Terminal.Gui/Views/ComboBox.cs

@@ -299,7 +299,7 @@ public class ComboBox : View, IDesignable
     }
 
     /// <inheritdoc/>
-    public override bool OnEnter (View view)
+    protected override bool OnEnter (View view)
     {
         if (!_search.HasFocus && !_listview.HasFocus)
         {
@@ -308,14 +308,14 @@ public class ComboBox : View, IDesignable
 
         _search.CursorPosition = _search.Text.GetRuneCount ();
 
-        return base.OnEnter (view);
+        return false; // Don't cancel the focus switch
     }
 
     /// <summary>Virtual method which invokes the <see cref="Expanded"/> event.</summary>
     public virtual void OnExpanded () { Expanded?.Invoke (this, EventArgs.Empty); }
 
     /// <inheritdoc/>
-    public override bool OnLeave (View view)
+    protected override bool OnLeave (View view)
     {
         if (_source?.Count > 0
             && _selectedItem > -1
@@ -335,7 +335,7 @@ public class ComboBox : View, IDesignable
             _listview.TabStop = TabBehavior.NoStop;
         }
 
-        return base.OnLeave (view);
+        return false; // Don't cancel the focus switch
     }
 
     /// <summary>Invokes the OnOpenSelectedItem event if it is defined.</summary>
@@ -940,7 +940,7 @@ public class ComboBox : View, IDesignable
             }
         }
 
-        public override bool OnEnter (View view)
+        protected override bool OnEnter (View view)
         {
             if (_hideDropdownListOnClick)
             {
@@ -949,10 +949,10 @@ public class ComboBox : View, IDesignable
                 Application.GrabMouse (this);
             }
 
-            return base.OnEnter (view);
+            return false; // Don't cancel the focus switch
         }
 
-        public override bool OnLeave (View view)
+        protected override bool OnLeave (View view)
         {
             if (_hideDropdownListOnClick)
             {
@@ -961,7 +961,7 @@ public class ComboBox : View, IDesignable
                 Application.UngrabMouse ();
             }
 
-            return base.OnLeave (view);
+            return false; // Don't cancel the focus switch
         }
 
         public override bool OnSelectedChanged ()

+ 2 - 2
Terminal.Gui/Views/ListView.cs

@@ -739,14 +739,14 @@ public class ListView : View, IDesignable
     }
 
     /// <inheritdoc/>
-    public override bool OnEnter (View view)
+    protected override bool OnEnter (View view)
     {
         if (_lastSelectedItem != _selected)
         {
             EnsureSelectedItemVisible ();
         }
 
-        return base.OnEnter (view);
+        return false; // Don't cancel the focus switch
     }
 
     // TODO: This should be cancelable

+ 6 - 1
Terminal.Gui/Views/Menu/Menu.cs

@@ -587,7 +587,12 @@ internal sealed class Menu : View
         _host.Run (action);
     }
 
-    public override bool OnLeave (View view) { return _host.OnLeave (view); }
+    protected override bool OnLeave (View view)
+    {
+        _host.LostFocus (view);
+
+        return false;
+    }
 
     private void RunSelected ()
     {

+ 2 - 2
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -1329,14 +1329,14 @@ public class MenuBar : View, IDesignable
     #region Mouse Handling
 
     /// <inheritdoc/>
-    public override bool OnLeave (View view)
+    internal void LostFocus (View view)
     {
         if (((!(view is MenuBar) && !(view is Menu)) || (!(view is MenuBar) && !(view is Menu) && _openMenu is { })) && !_isCleaning && !_reopen)
         {
             CleanUp ();
         }
 
-        return base.OnLeave (view);
+        return;
     }
 
     /// <inheritdoc/>

+ 4 - 4
Terminal.Gui/Views/Shortcut.cs

@@ -835,21 +835,21 @@ public class Shortcut : View, IOrientation, IDesignable
     private View _lastFocusedView;
 
     /// <inheritdoc/>
-    public override bool OnEnter (View view)
+    protected override bool OnEnter (View view)
     {
         SetColors ();
         _lastFocusedView = view;
 
-        return base.OnEnter (view);
+        return false; // Don't cancel the focus switch
     }
 
     /// <inheritdoc/>
-    public override bool OnLeave (View view)
+    protected override bool OnLeave (View view)
     {
         SetColors ();
         _lastFocusedView = this;
 
-        return base.OnLeave (view);
+        return false; // Don't cancel the focus switch
     }
 
     #endregion Focus

+ 2 - 2
Terminal.Gui/Views/TextField.cs

@@ -1034,7 +1034,7 @@ public class TextField : View
     }
 
     /// <inheritdoc/>
-    public override bool OnLeave (View view)
+    protected override bool OnLeave (View view)
     {
         if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
         {
@@ -1044,7 +1044,7 @@ public class TextField : View
         //if (SelectedLength != 0 && !(Application.MouseGrabView is MenuBar))
         //	ClearAllSelection ();
 
-        return base.OnLeave (view);
+        return false; // Don't cancel the focus switch
     }
 
     /// TODO: Flush out these docs

+ 2 - 2
Terminal.Gui/Views/TextView.cs

@@ -3650,14 +3650,14 @@ public class TextView : View
     }
 
     /// <inheritdoc/>
-    public override bool OnLeave (View view)
+    protected override bool OnLeave (View view)
     {
         if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
         {
             Application.UngrabMouse ();
         }
 
-        return base.OnLeave (view);
+        return false; // Don't cancel the focus switch
     }
 
     /// <inheritdoc/>

+ 8 - 5
Terminal.Gui/Views/Toplevel.cs

@@ -411,11 +411,14 @@ public partial class Toplevel : View
 
     #region Navigation
 
-    /// <inheritdoc/>
-    public override bool OnEnter (View view) { return MostFocused?.OnEnter (view) ?? base.OnEnter (view); }
-
-    /// <inheritdoc/>
-    public override bool OnLeave (View view) { return MostFocused?.OnLeave (view) ?? base.OnLeave (view); }
+    ///// <inheritdoc/>
+    //protected override bool OnEnter (View view)
+    //{
+    //    return MostFocused?.OnEnter (view) ?? false;
+    //}
+
+    ///// <inheritdoc/>
+    //protected override bool OnLeave (View view) { return MostFocused?.OnLeave (view) ?? false; }
 
     #endregion
 

+ 2 - 2
Terminal.Gui/Views/TreeView/TreeView.cs

@@ -1155,14 +1155,14 @@ public class TreeView<T> : View, ITreeView where T : class
     }
 
     ///<inheritdoc/>
-    public override bool OnEnter (View view)
+    protected override bool OnEnter (View view)
     {
         if (SelectedObject is null && Objects.Any ())
         {
             SelectedObject = Objects.First ();
         }
 
-        return base.OnEnter (view);
+        return false; // Don't cancel the focus switch
     }
 
     /// <inheritdoc/>

+ 0 - 12
UICatalog/Scenarios/AdornmentsEditor.cs

@@ -185,17 +185,5 @@ public class AdornmentsEditor : View
         {
             return;
         }
-
-            _viewToEdit = value;
-
-
-            _marginEditor.AdornmentToEdit = _viewToEdit?.Margin ?? null;
-            _borderEditor.AdornmentToEdit = _viewToEdit?.Border ?? null;
-            _paddingEditor.AdornmentToEdit = _viewToEdit?.Padding ?? null;
-
-            _lblView.Text = $"{_viewToEdit?.GetType ().Name}: {_viewToEdit?.Id}"  ?? string.Empty;
-
-            return;
-        }
     }
 }

+ 5 - 5
UnitTests/Application/Application.NavigationTests.cs

@@ -13,16 +13,16 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
     {
         bool raised = false;
 
-        ApplicationNavigation.FocusedChanged += ApplicationNavigationOnFocusedChanged;
+        Application.Navigation.FocusedChanged += ApplicationNavigationOnFocusedChanged;
 
-        ApplicationNavigation.Focused = new View();
+        Application.Navigation.SetFocused(new View ());
 
         Assert.True (raised);
 
-        ApplicationNavigation.Focused.Dispose ();
-        ApplicationNavigation.Focused = null;
+        Application.Navigation.GetFocused().Dispose ();
+        Application.Navigation.SetFocused(null);
 
-        ApplicationNavigation.FocusedChanged -= ApplicationNavigationOnFocusedChanged;
+        Application.Navigation.FocusedChanged -= ApplicationNavigationOnFocusedChanged;
 
         return;
 

+ 0 - 8
UnitTests/View/ViewTests.cs

@@ -877,14 +877,6 @@ At 0,0
         Assert.False (r.NewMouseEnterEvent (new() { Flags = MouseFlags.AllEvents }));
         Assert.False (r.NewMouseLeaveEvent (new() { Flags = MouseFlags.AllEvents }));
 
-        var v1 = new View ();
-        Assert.False (r.OnEnter (v1));
-        v1.Dispose ();
-
-        var v2 = new View ();
-        Assert.False (r.OnLeave (v2));
-        v2.Dispose ();
-
         r.Dispose ();
 
         // TODO: Add more