소스 검색

WIP (Very Broken) try to move keybindings out of Toplevel to app

Tig 1 년 전
부모
커밋
feaf5c0f6c

+ 1 - 1
Terminal.Gui/Application/Application.Initialization.cs

@@ -89,7 +89,7 @@ public static partial class Application // Initialization (Init/Shutdown)
         Load ();
         Apply ();
 
-        AddToplevelKeyBindings ();
+        AddApplicationKeyBindings ();
 
         // Ignore Configuration for ForceDriver if driverName is specified
         if (!string.IsNullOrEmpty (driverName))

+ 163 - 44
Terminal.Gui/Application/Application.Keyboard.cs

@@ -213,61 +213,172 @@ public static partial class Application // Keyboard handling
         return false;
     }
 
-    /// <summary>
-    ///     The <see cref="KeyBindingScope.Application"/> key bindings.
-    /// </summary>
-    private static readonly Dictionary<Key, List<View?>> _keyBindings = new ();
+    /// <summary>Gets the key bindings for this view.</summary>
+    public static KeyBindings KeyBindings { get; internal set; } = new ();
 
     /// <summary>
-    /// Gets the list of <see cref="KeyBindingScope.Application"/> key bindings.
+    /// Commands for Application.
     /// </summary>
-    public static Dictionary<Key, List<View?>> GetKeyBindings () { return _keyBindings; }
+    private static Dictionary<Command, Func<CommandContext, bool?>> CommandImplementations { get; } = new ();
 
     /// <summary>
-    ///     Adds an  <see cref="KeyBindingScope.Application"/> scoped key binding.
+    ///     <para>
+    ///         Sets the function that will be invoked for a <see cref="Command"/>. 
+    ///     </para>
+    ///     <para>
+    ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
+    ///         replace the old one.
+    ///     </para>
     /// </summary>
     /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
+    /// <para>
+    ///     This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
+    /// </para>
     /// </remarks>
-    /// <param name="key">The key being bound.</param>
-    /// <param name="view">The view that is bound to the key. If <see langword="null"/>, <see cref="Application.Current"/> will be used.</param>
-    internal static void AddKeyBinding (Key key, View? view)
+    /// <param name="command">The command.</param>
+    /// <param name="f">The function.</param>
+    private static void AddCommand (Command command, Func<bool?> f)
     {
-        if (!_keyBindings.ContainsKey (key))
-        {
-            _keyBindings [key] = [];
-        }
-
-        _keyBindings [key].Add (view);
+        CommandImplementations [command] = ctx => f ();
     }
 
-    internal static void AddToplevelKeyBindings ()
+    ///// <summary>
+    /////     The <see cref="KeyBindingScope.Application"/> key bindings.
+    ///// </summary>
+    //private static readonly Dictionary<Key, List<View?>> _keyBindings = new ();
+
+    ///// <summary>
+    ///// Gets the list of <see cref="KeyBindingScope.Application"/> key bindings.
+    ///// </summary>
+    //public static Dictionary<Key, List<View?>> GetKeyBindings () { return _keyBindings; }
+
+    ///// <summary>
+    /////     Adds an  <see cref="KeyBindingScope.Application"/> scoped key binding.
+    ///// </summary>
+    ///// <remarks>
+    /////     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
+    ///// </remarks>
+    ///// <param name="key">The key being bound.</param>
+    ///// <param name="view">The view that is bound to the key. If <see langword="null"/>, <see cref="Application.Current"/> will be used.</param>
+    //internal static void AddKeyBinding (Key key, View? view)
+    //{
+    //    if (!_keyBindings.ContainsKey (key))
+    //    {
+    //        _keyBindings [key] = [];
+    //    }
+
+    //    _keyBindings [key].Add (view);
+    //}
+
+    internal static void AddApplicationKeyBindings ()
     {
-//        // Default keybindings for this view
-//        AddKeyBinding (Application.QuitKey, null);
-//        AddKeyBinding (Key.CursorRight, null);
-//        AddKeyBinding (Key.CursorDown, null);
-//        AddKeyBinding (Key.CursorLeft, null);
-//        AddKeyBinding (Key.CursorUp, null);
-//        AddKeyBinding (Key.Tab, null);
-//        AddKeyBinding (Key.Tab.WithShift, null);
-//        AddKeyBinding (Key.Tab.WithCtrl, null);
-//        AddKeyBinding (Key.Tab.WithShift.WithCtrl, null);
-//        AddKeyBinding (Key.F5, null);
-//        AddKeyBinding (Application.AlternateForwardKey, null); // Needed on Unix
-//        AddKeyBinding (Application.AlternateBackwardKey, null); // Needed on Unix
-
-//        if (Environment.OSVersion.Platform == PlatformID.Unix)
-//        {
-//            AddKeyBinding (Key.Z.WithCtrl, null);
-//        }
-
-//#if UNIX_KEY_BINDINGS
-//        KeyBindings.Add (Key.L.WithCtrl, Command.Refresh); // Unix
-//        KeyBindings.Add (Key.F.WithCtrl, Command.NextView); // Unix
-//        KeyBindings.Add (Key.I.WithCtrl, Command.NextView); // Unix
-//        KeyBindings.Add (Key.B.WithCtrl, Command.PreviousView); // Unix
-//#endif
+        // Things this view knows how to do
+        AddCommand (
+                    Command.QuitToplevel,  // TODO: IRunnable: Rename to Command.Quit to make more generic.
+                    () =>
+                    {
+                        if (OverlappedTop is { })
+                        {
+                            RequestStop (Current);
+                        }
+                        else
+                        {
+                            Application.RequestStop ();
+                        }
+
+                        return true;
+                    }
+                   );
+
+        AddCommand (
+                    Command.Suspend,
+                    () =>
+                    {
+                        Driver?.Suspend ();
+
+                        return true;
+                    }
+                   );
+
+        AddCommand (
+                    Command.NextView,    // TODO: Figure out how to move this to the View that is at the root of the view hierarchy (currently Application.Top)
+                    () =>
+                    {
+                        Current.MoveNextView ();
+
+                        return true;
+                    }
+                   );
+
+        AddCommand (
+                    Command.PreviousView,// TODO: Figure out how to move this to the View that is at the root of the view hierarchy (currently Application.Top)
+                    () =>
+                    {
+                        Current.MovePreviousView ();
+
+                        return true;
+                    }
+                   );
+
+        AddCommand (
+                    Command.NextViewOrTop,// TODO: Figure out how to move this to the View that is at the root of the view hierarchy (currently Application.Top)
+                    () =>
+                    {
+                        Current.MoveNextViewOrTop ();
+
+                        return true;
+                    }
+                   );
+
+        AddCommand (
+                    Command.PreviousViewOrTop,// TODO: Figure out how to move this to the View that is at the root of the view hierarchy (currently Application.Top)
+                    () =>
+                    {
+                        Current.MovePreviousViewOrTop ();
+
+                        return true;
+                    }
+                   );
+
+        AddCommand (
+                    Command.Refresh,
+                    () =>
+                    {
+                        Refresh (); 
+
+                        return true;
+                    }
+                   );
+
+
+        KeyBindings.Add (Application.QuitKey, KeyBindingScope.Application, Command.QuitToplevel);
+
+        KeyBindings.Add (Key.CursorRight, KeyBindingScope.Application, Command.NextView);
+        KeyBindings.Add (Key.CursorDown, KeyBindingScope.Application, Command.NextView);
+        KeyBindings.Add (Key.CursorLeft, KeyBindingScope.Application, Command.PreviousView);
+        KeyBindings.Add (Key.CursorUp, KeyBindingScope.Application, Command.PreviousView);
+
+        KeyBindings.Add (Key.Tab, KeyBindingScope.Application, Command.NextView);
+        KeyBindings.Add (Key.Tab.WithShift, KeyBindingScope.Application, Command.PreviousView);
+        KeyBindings.Add (Key.Tab.WithCtrl, KeyBindingScope.Application, Command.NextViewOrTop);
+        KeyBindings.Add (Key.Tab.WithShift.WithCtrl, KeyBindingScope.Application, Command.PreviousViewOrTop);
+
+        // TODO: Refresh Key should be configurable
+        KeyBindings.Add (Key.F5, KeyBindingScope.Application, Command.Refresh);
+        KeyBindings.Add (Application.AlternateForwardKey, KeyBindingScope.Application, Command.NextViewOrTop); // Needed on Unix
+        KeyBindings.Add (Application.AlternateBackwardKey, KeyBindingScope.Application, Command.PreviousViewOrTop); // Needed on Unix
+
+        if (Environment.OSVersion.Platform == PlatformID.Unix)
+        {
+            KeyBindings.Add (Key.Z.WithCtrl, Command.Suspend);
+        }
+
+#if UNIX_KEY_BINDINGS
+        KeyBindings.Add (Key.L.WithCtrl, Command.Refresh); // Unix
+        KeyBindings.Add (Key.F.WithCtrl, Command.NextView); // Unix
+        KeyBindings.Add (Key.I.WithCtrl, Command.NextView); // Unix
+        KeyBindings.Add (Key.B.WithCtrl, Command.PreviousView); // Unix
+#endif
     }
 
     /// <summary>
@@ -277,7 +388,15 @@ public static partial class Application // Keyboard handling
     ///     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
     /// </remarks>
     /// <returns>The list of Views that have Application-scoped key bindings.</returns>
-    internal static List<View> GetViewsWithKeyBindings () { return _keyBindings.Values.SelectMany (v => v).ToList (); }
+    internal static List<KeyBinding> GetViewKeyBindings ()
+    {
+        // Get the list of views that do not have Application-scoped key bindings
+        return KeyBindings.Bindings
+                          .Where (kv => kv.Value.Scope != KeyBindingScope.Application)
+                          .Select (kv => kv.Value)
+                          .Distinct ()
+                          .ToList ();
+    }
 
     /// <summary>
     ///     Gets the list of Views that have <see cref="KeyBindingScope.Application"/> key bindings for the specified key.

+ 14 - 5
Terminal.Gui/Input/KeyBindings.cs

@@ -11,7 +11,7 @@ public class KeyBindings
 {
     /// <summary>
     ///     Initializes a new instance. This constructor is used when the <see cref="KeyBindings"/> are not bound to a
-    ///     <see cref="View"/>, such as in unit tests.
+    ///     <see cref="View"/>. This is used for Application.KeyBindings and unit tests.
     /// </summary>
     public KeyBindings () { }
 
@@ -21,6 +21,9 @@ public class KeyBindings
     /// <summary>
     ///     The view that the <see cref="KeyBindings"/> are bound to.
     /// </summary>
+    /// <remarks>
+    ///     If <see langword="null"/>, the <see cref="KeyBindings"/> are not bound to a <see cref="View"/>. This is used for Application.KeyBindings.
+    /// </remarks>
     public View? BoundView { get; }
 
     // TODO: Add a dictionary comparer that ignores Scope
@@ -33,6 +36,11 @@ public class KeyBindings
     /// <param name="binding"></param>
     public void Add (Key key, KeyBinding binding)
     {
+        if (BoundView is { } && binding.Scope.FastHasFlags (KeyBindingScope.Application))
+        {
+            throw new ArgumentException ("Application scoped KeyBindings must be added via Application.KeyBindings.Add");
+        }
+
         if (TryGet (key, out KeyBinding _))
         {
             Bindings [key] = binding;
@@ -40,10 +48,6 @@ public class KeyBindings
         else
         {
             Bindings.Add (key, binding);
-            if (binding.Scope.HasFlag (KeyBindingScope.Application))
-            {
-                Application.AddKeyBinding (key, BoundView);
-            }
         }
     }
 
@@ -67,6 +71,11 @@ public class KeyBindings
     /// </param>
     public void Add (Key key, KeyBindingScope scope, params Command [] commands)
     {
+        if (BoundView is { } && scope.FastHasFlags (KeyBindingScope.Application))
+        {
+            throw new ArgumentException ("Application scoped KeyBindings must be added via Application.KeyBindings.Add");
+        }
+
         if (key is null || !key.IsValid)
         {
             //throw new ArgumentException ("Invalid Key", nameof (commands));

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

@@ -641,7 +641,7 @@ public partial class View
     /// </returns>
     public virtual bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope)
     {
-        // fire event only if there's an hotkey binding for the key
+        // fire event only if there's a hotkey binding for the key
         if (KeyBindings.TryGet (keyEvent, scope, out KeyBinding kb))
         {
             InvokingKeyBindings?.Invoke (this, keyEvent);