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

This commit is contained in:
Tig
2024-07-23 18:12:43 -06:00
parent f8e8aff29f
commit feaf5c0f6c
4 changed files with 177 additions and 49 deletions

View File

@@ -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))

View File

@@ -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
// 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 ();
}
// if (Environment.OSVersion.Platform == PlatformID.Unix)
// {
// AddKeyBinding (Key.Z.WithCtrl, null);
// }
return true;
}
);
//#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
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.

View File

@@ -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));

View File

@@ -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);