Browse Source

Upgraded Shortcut

Tig 1 year ago
parent
commit
16b374bae4
1 changed files with 317 additions and 286 deletions
  1. 317 286
      Terminal.Gui/Views/Shortcut.cs

+ 317 - 286
Terminal.Gui/Views/Shortcut.cs

@@ -1,10 +1,8 @@
-using System.ComponentModel;
-using System.Threading.Channels;
-
-namespace Terminal.Gui;
+namespace Terminal.Gui;
 
 /// <summary>
-///     Displays a command, help text, and a key binding. When the key specified by <see cref="Key"/> is pressed, the command will be invoked. Useful for
+///     Displays a command, help text, and a key binding. When the key specified by <see cref="Key"/> is pressed, the
+///     command will be invoked. Useful for
 ///     displaying a command in <see cref="Bar"/> such as a
 ///     menu, toolbar, or status bar.
 /// </summary>
@@ -12,12 +10,13 @@ namespace Terminal.Gui;
 ///     <para>
 ///         The following user actions will invoke the <see cref="Command.Accept"/>, causing the
 ///         <see cref="View.Accept"/> event to be fired:
-/// - Clicking on the <see cref="Shortcut"/>.
-/// - Pressing the key specified by <see cref="Key"/>.
-/// - Pressing the HotKey specified by <see cref="CommandView"/>.
+///         - Clicking on the <see cref="Shortcut"/>.
+///         - Pressing the key specified by <see cref="Key"/>.
+///         - Pressing the HotKey specified by <see cref="CommandView"/>.
 ///     </para>
 ///     <para>
-///         If <see cref="KeyBindingScope"/> is <see cref="KeyBindingScope.Application"/>, <see cref="Key"/> will invoked <see cref="Command.Accept"/>
+///         If <see cref="KeyBindingScope"/> is <see cref="KeyBindingScope.Application"/>, <see cref="Key"/> will invoked
+///         <see cref="Command.Accept"/>
 ///         command regardless of what View has focus, enabling an application-wide keyboard shortcut.
 ///     </para>
 ///     <para>
@@ -37,8 +36,10 @@ namespace Terminal.Gui;
 ///         If the <see cref="Key"/> is <see cref="Key.Empty"/>, the <see cref="Key"/> text is not displayed.
 ///     </para>
 /// </remarks>
-public class Shortcut : View
+public class Shortcut : View, IOrientation, IDesignable
 {
+    private readonly OrientationHelper _orientationHelper;
+
     /// <summary>
     ///     Creates a new instance of <see cref="Shortcut"/>.
     /// </summary>
@@ -60,6 +61,10 @@ public class Shortcut : View
         Width = GetWidthDimAuto ();
         Height = Dim.Auto (DimAutoStyle.Content, 1);
 
+        _orientationHelper = new (this);
+        _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
+        _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
+
         AddCommand (Command.HotKey, ctx => OnAccept (ctx));
         AddCommand (Command.Accept, ctx => OnAccept (ctx));
         AddCommand (Command.Select, ctx => OnSelect (ctx));
@@ -132,31 +137,49 @@ public class Shortcut : View
         }
     }
 
-
     /// <summary>
     ///     Creates a new instance of <see cref="Shortcut"/>.
     /// </summary>
     public Shortcut () : this (Key.Empty, string.Empty, null) { }
 
-    private Orientation _orientation = Orientation.Horizontal;
+    #region IOrientation members
 
     /// <summary>
-    ///     Gets or sets the <see cref="Orientation"/> for this <see cref="Shortcut"/>. The default is
-    ///     <see cref="Orientation.Horizontal"/>, which is ideal for status bar, menu bar, and tool bar items If set to
-    ///     <see cref="Orientation.Vertical"/>,
-    ///     the Shortcut will be configured for vertical layout, which is ideal for menu items.
+    ///     Gets or sets the <see cref="Orientation"/> for this <see cref="Bar"/>. The default is
+    ///     <see cref="Orientation.Horizontal"/>.
     /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Horizontal orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from right to
+    ///         left
+    ///         Vertical orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from left to
+    ///         right.
+    ///     </para>
+    /// </remarks>
+
     public Orientation Orientation
     {
-        get => _orientation;
-        set
-        {
-            _orientation = value;
+        get => _orientationHelper.Orientation;
+        set => _orientationHelper.Orientation = value;
+    }
 
-            // TODO: Determine what, if anything, is opinionated about the orientation.
-        }
+    /// <inheritdoc/>
+    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+
+    /// <inheritdoc/>
+    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanged;
+
+    /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
+    /// <param name="oldOrientation"></param>
+    /// <param name="newOrientation"></param>
+    public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation)
+    {
+        // TODO: Determine what, if anything, is opinionated about the orientation.
+        SetNeedsLayout ();
     }
 
+    #endregion
+
     private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast;
 
     /// <summary>
@@ -344,7 +367,6 @@ public class Shortcut : View
     private void Subview_MouseClick (object sender, MouseEventEventArgs e)
     {
         // TODO: Remove. This does nothing.
-        return;
     }
 
     #region Command
@@ -434,359 +456,368 @@ public class Shortcut : View
             SetKeyViewDefaultLayout ();
             ShowHide ();
             UpdateKeyBinding ();
-
-            return;
         }
     }
 
     private void SetCommandViewDefaultLayout ()
-{
-    CommandView.Margin.Thickness = GetMarginThickness ();
-    CommandView.X = Pos.Align (Alignment.End, AlignmentModes);
-    CommandView.Y = 0; //Pos.Center ();
-}
+    {
+        CommandView.Margin.Thickness = GetMarginThickness ();
+        CommandView.X = Pos.Align (Alignment.End, AlignmentModes);
+        CommandView.Y = 0; //Pos.Center ();
+    }
 
-private void Shortcut_TitleChanged (object sender, EventArgs<string> e)
-{
-    // If the Title changes, update the CommandView text.
-    // This is a helper to make it easier to set the CommandView text.
-    // CommandView is public and replaceable, but this is a convenience.
-    _commandView.Text = Title;
-}
+    private void Shortcut_TitleChanged (object sender, EventArgs<string> e)
+    {
+        // If the Title changes, update the CommandView text.
+        // This is a helper to make it easier to set the CommandView text.
+        // CommandView is public and replaceable, but this is a convenience.
+        _commandView.Text = Title;
+    }
 
-#endregion Command
+    #endregion Command
 
-#region Help
+    #region Help
 
-/// <summary>
-///     The subview that displays the help text for the command. Internal for unit testing.
-/// </summary>
-internal View HelpView { get; } = new ();
+    /// <summary>
+    ///     The subview that displays the help text for the command. Internal for unit testing.
+    /// </summary>
+    internal View HelpView { get; } = new ();
 
-private void SetHelpViewDefaultLayout ()
-{
-    HelpView.Margin.Thickness = GetMarginThickness ();
-    HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
-    HelpView.Y = 0; //Pos.Center ();
-    HelpView.Width = Dim.Auto (DimAutoStyle.Text);
-    HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
-
-    HelpView.Visible = true;
-    HelpView.VerticalTextAlignment = Alignment.Center;
-}
+    private void SetHelpViewDefaultLayout ()
+    {
+        HelpView.Margin.Thickness = GetMarginThickness ();
+        HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
+        HelpView.Y = 0; //Pos.Center ();
+        HelpView.Width = Dim.Auto (DimAutoStyle.Text);
+        HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
+
+        HelpView.Visible = true;
+        HelpView.VerticalTextAlignment = Alignment.Center;
+    }
 
-/// <summary>
-///     Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
-///     .
-/// </summary>
-public override string Text
-{
-    get => HelpView?.Text;
-    set
+    /// <summary>
+    ///     Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
+    ///     .
+    /// </summary>
+    public override string Text
     {
-        if (HelpView != null)
+        get => HelpView?.Text;
+        set
         {
-            HelpView.Text = value;
-            ShowHide ();
+            if (HelpView != null)
+            {
+                HelpView.Text = value;
+                ShowHide ();
+            }
         }
     }
-}
 
-/// <summary>
-///     Gets or sets the help text displayed in the middle of the Shortcut.
-/// </summary>
-public string HelpText
-{
-    get => HelpView?.Text;
-    set
+    /// <summary>
+    ///     Gets or sets the help text displayed in the middle of the Shortcut.
+    /// </summary>
+    public string HelpText
     {
-        if (HelpView != null)
+        get => HelpView?.Text;
+        set
         {
-            HelpView.Text = value;
-            ShowHide ();
+            if (HelpView != null)
+            {
+                HelpView.Text = value;
+                ShowHide ();
+            }
         }
     }
-}
 
-#endregion Help
+    #endregion Help
 
-#region Key
+    #region Key
 
-private Key _key = Key.Empty;
+    private Key _key = Key.Empty;
 
-/// <summary>
-///     Gets or sets the <see cref="Key"/> that will be bound to the <see cref="Command.Accept"/> command.
-/// </summary>
-public Key Key
-{
-    get => _key;
-    set
+    /// <summary>
+    ///     Gets or sets the <see cref="Key"/> that will be bound to the <see cref="Command.Accept"/> command.
+    /// </summary>
+    public Key Key
     {
-        if (value == null)
+        get => _key;
+        set
         {
-            throw new ArgumentNullException ();
-        }
+            if (value == null)
+            {
+                throw new ArgumentNullException ();
+            }
 
-        _key = value;
+            _key = value;
 
-        UpdateKeyBinding ();
+            UpdateKeyBinding ();
 
-        KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
-        ShowHide ();
+            KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
+            ShowHide ();
+        }
     }
-}
 
-private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey;
+    private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey;
 
-/// <summary>
-///     Gets or sets the scope for the key binding for how <see cref="Key"/> is bound to <see cref="Command"/>.
-/// </summary>
-public KeyBindingScope KeyBindingScope
-{
-    get => _keyBindingScope;
-    set
+    /// <summary>
+    ///     Gets or sets the scope for the key binding for how <see cref="Key"/> is bound to <see cref="Command"/>.
+    /// </summary>
+    public KeyBindingScope KeyBindingScope
     {
-        _keyBindingScope = value;
+        get => _keyBindingScope;
+        set
+        {
+            _keyBindingScope = value;
 
-        UpdateKeyBinding ();
+            UpdateKeyBinding ();
+        }
     }
-}
 
-/// <summary>
-///     Gets the subview that displays the key. Internal for unit testing.
-/// </summary>
+    /// <summary>
+    ///     Gets the subview that displays the key. Internal for unit testing.
+    /// </summary>
 
-internal View KeyView { get; } = new ();
+    internal View KeyView { get; } = new ();
 
-private int _minimumKeyTextSize;
+    private int _minimumKeyTextSize;
 
-/// <summary>
-/// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
-/// </summary>
-public int MinimumKeyTextSize
-{
-    get => _minimumKeyTextSize;
-    set
+    /// <summary>
+    ///     Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
+    /// </summary>
+    public int MinimumKeyTextSize
     {
-        if (value == _minimumKeyTextSize)
+        get => _minimumKeyTextSize;
+        set
         {
-            //return;
-        }
+            if (value == _minimumKeyTextSize)
+            {
+                //return;
+            }
 
-        _minimumKeyTextSize = value;
-        SetKeyViewDefaultLayout ();
-        CommandView.SetNeedsLayout ();
-        HelpView.SetNeedsLayout ();
-        KeyView.SetNeedsLayout ();
-        SetSubViewNeedsDisplay ();
+            _minimumKeyTextSize = value;
+            SetKeyViewDefaultLayout ();
+            CommandView.SetNeedsLayout ();
+            HelpView.SetNeedsLayout ();
+            KeyView.SetNeedsLayout ();
+            SetSubViewNeedsDisplay ();
+        }
     }
-}
-
-private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; }
 
-private void SetKeyViewDefaultLayout ()
-{
-    KeyView.Margin.Thickness = GetMarginThickness ();
-    KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
-    KeyView.Y = 0; //Pos.Center ();
-    KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize));
-    KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
-
-    KeyView.Visible = true;
-
-    // Right align the text in the keyview
-    KeyView.TextAlignment = Alignment.End;
-    KeyView.VerticalTextAlignment = Alignment.Center;
-    KeyView.KeyBindings.Clear ();
-}
+    private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; }
 
-private void UpdateKeyBinding ()
-{
-    if (Key != null)
+    private void SetKeyViewDefaultLayout ()
     {
-        // Disable the command view key bindings
-        CommandView.KeyBindings.Remove (Key);
-        CommandView.KeyBindings.Remove (CommandView.HotKey);
-        KeyBindings.Remove (Key);
-        KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
-        //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept);
+        KeyView.Margin.Thickness = GetMarginThickness ();
+        KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
+        KeyView.Y = 0; //Pos.Center ();
+        KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize));
+        KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
+
+        KeyView.Visible = true;
+
+        // Right align the text in the keyview
+        KeyView.TextAlignment = Alignment.End;
+        KeyView.VerticalTextAlignment = Alignment.Center;
+        KeyView.KeyBindings.Clear ();
     }
-}
-
-#endregion Key
 
-#region Accept Handling
+    private void UpdateKeyBinding ()
+    {
+        if (Key != null)
+        {
+            // Disable the command view key bindings
+            CommandView.KeyBindings.Remove (Key);
+            CommandView.KeyBindings.Remove (CommandView.HotKey);
+            KeyBindings.Remove (Key);
+            KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
 
-/// <summary>
-///     Called when the <see cref="Command.Accept"/> command is received. This
-///     occurs
-///     - if the user clicks anywhere on the shortcut with the mouse
-///     - if the user presses Key
-///     - if the user presses the HotKey specified by CommandView
-///     - if HasFocus and the user presses Space or Enter (or any other key bound to Command.Accept).
-/// </summary>
-protected bool? OnAccept (CommandContext ctx)
-{
-    var cancel = false;
+            //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept);
+        }
+    }
 
-    switch (ctx.KeyBinding?.Scope)
-    {
-        case KeyBindingScope.Application:
-            cancel = base.OnAccept () == true;
+    #endregion Key
 
-            break;
+    #region Accept Handling
 
-        case KeyBindingScope.Focused:
-            base.OnAccept ();
+    /// <summary>
+    ///     Called when the <see cref="Command.Accept"/> command is received. This
+    ///     occurs
+    ///     - if the user clicks anywhere on the shortcut with the mouse
+    ///     - if the user presses Key
+    ///     - if the user presses the HotKey specified by CommandView
+    ///     - if HasFocus and the user presses Space or Enter (or any other key bound to Command.Accept).
+    /// </summary>
+    protected bool? OnAccept (CommandContext ctx)
+    {
+        var cancel = false;
 
-            // cancel if we're focused
-            cancel = true;
+        switch (ctx.KeyBinding?.Scope)
+        {
+            case KeyBindingScope.Application:
+                cancel = base.OnAccept () == true;
 
-            break;
+                break;
 
-        case KeyBindingScope.HotKey:
-            cancel = base.OnAccept () == true;
+            case KeyBindingScope.Focused:
+                base.OnAccept ();
 
-            if (CanFocus)
-            {
-                SetFocus ();
+                // cancel if we're focused
                 cancel = true;
-            }
 
-            break;
+                break;
 
-        default:
-            // Mouse
-            cancel = base.OnAccept () == true;
+            case KeyBindingScope.HotKey:
+                cancel = base.OnAccept () == true;
 
-            break;
-    }
+                if (CanFocus)
+                {
+                    SetFocus ();
+                    cancel = true;
+                }
 
-    CommandView.InvokeCommand (Command.Accept, ctx.Key, ctx.KeyBinding);
+                break;
 
-    if (Action is { })
-    {
-        Action.Invoke ();
-        // Assume if there's a subscriber to Action, it's handled.
-        cancel = true;
-    }
+            default:
+                // Mouse
+                cancel = base.OnAccept () == true;
 
-    return cancel;
-}
+                break;
+        }
 
-/// <summary>
-///     Gets or sets the action to be invoked when the shortcut key is pressed or the shortcut is clicked on with the
-///     mouse.
-/// </summary>
-/// <remarks>
-///     Note, the <see cref="View.Accept"/> event is fired first, and if cancelled, the event will not be invoked.
-/// </remarks>
-[CanBeNull]
-public Action Action { get; set; }
+        CommandView.InvokeCommand (Command.Accept, ctx.Key, ctx.KeyBinding);
 
-#endregion Accept Handling
+        if (Action is { })
+        {
+            Action.Invoke ();
 
-private bool? OnSelect (CommandContext ctx)
-{
-    if (CommandView.GetSupportedCommands ().Contains (Command.Select))
-    {
-        return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
-    }
-    return false;
+            // Assume if there's a subscriber to Action, it's handled.
+            cancel = true;
+        }
 
-}
+        return cancel;
+    }
 
+    /// <summary>
+    ///     Gets or sets the action to be invoked when the shortcut key is pressed or the shortcut is clicked on with the
+    ///     mouse.
+    /// </summary>
+    /// <remarks>
+    ///     Note, the <see cref="View.Accept"/> event is fired first, and if cancelled, the event will not be invoked.
+    /// </remarks>
+    [CanBeNull]
+    public Action Action { get; set; }
 
-#region Focus
+    #endregion Accept Handling
 
-/// <inheritdoc/>
-public override ColorScheme ColorScheme
-{
-    get => base.ColorScheme;
-    set
+    private bool? OnSelect (CommandContext ctx)
     {
-        base.ColorScheme = value;
-        SetColors ();
+        if (CommandView.GetSupportedCommands ().Contains (Command.Select))
+        {
+            return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
+        }
+
+        return false;
     }
-}
 
-/// <summary>
-/// </summary>
-internal void SetColors ()
-{
-    // Border should match superview.
-    Border.ColorScheme = SuperView?.ColorScheme;
+    #region Focus
 
-    if (HasFocus)
+    /// <inheritdoc/>
+    public override ColorScheme ColorScheme
     {
-        // When we have focus, we invert the colors
-        base.ColorScheme = new (base.ColorScheme)
+        get => base.ColorScheme;
+        set
         {
-            Normal = base.ColorScheme.Focus,
-            HotNormal = base.ColorScheme.HotFocus,
-            HotFocus = base.ColorScheme.HotNormal,
-            Focus = base.ColorScheme.Normal
-        };
-    }
-    else
-    {
-        base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme;
+            base.ColorScheme = value;
+            SetColors ();
+        }
     }
 
-    // Set KeyView's colors to show "hot"
-    if (IsInitialized && base.ColorScheme is { })
+    /// <summary>
+    /// </summary>
+    internal void SetColors ()
     {
-        var cs = new ColorScheme (base.ColorScheme)
+        // Border should match superview.
+        Border.ColorScheme = SuperView?.ColorScheme;
+
+        if (HasFocus)
         {
-            Normal = base.ColorScheme.HotNormal,
-            HotNormal = base.ColorScheme.Normal
-        };
-        KeyView.ColorScheme = cs;
+            // When we have focus, we invert the colors
+            base.ColorScheme = new (base.ColorScheme)
+            {
+                Normal = base.ColorScheme.Focus,
+                HotNormal = base.ColorScheme.HotFocus,
+                HotFocus = base.ColorScheme.HotNormal,
+                Focus = base.ColorScheme.Normal
+            };
+        }
+        else
+        {
+            base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme;
+        }
+
+        // Set KeyView's colors to show "hot"
+        if (IsInitialized && base.ColorScheme is { })
+        {
+            var cs = new ColorScheme (base.ColorScheme)
+            {
+                Normal = base.ColorScheme.HotNormal,
+                HotNormal = base.ColorScheme.Normal
+            };
+            KeyView.ColorScheme = cs;
+        }
     }
-}
 
-View _lastFocusedView;
-/// <inheritdoc/>
-public override bool OnEnter (View view)
-{
-    SetColors ();
-    _lastFocusedView = view;
+    private View _lastFocusedView;
 
-    return base.OnEnter (view);
-}
+    /// <inheritdoc/>
+    public override bool OnEnter (View view)
+    {
+        SetColors ();
+        _lastFocusedView = view;
 
-/// <inheritdoc/>
-public override bool OnLeave (View view)
-{
-    SetColors ();
-    _lastFocusedView = this;
+        return base.OnEnter (view);
+    }
 
-    return base.OnLeave (view);
-}
+    /// <inheritdoc/>
+    public override bool OnLeave (View view)
+    {
+        SetColors ();
+        _lastFocusedView = this;
 
-#endregion Focus
+        return base.OnLeave (view);
+    }
 
-/// <inheritdoc/>
-protected override void Dispose (bool disposing)
-{
-    if (disposing)
+    #endregion Focus
+
+    /// <inheritdoc/>
+    public bool EnableForDesign ()
     {
-        if (CommandView?.IsAdded == false)
-        {
-            CommandView.Dispose ();
-        }
+        Title = "_Shortcut";
+        HelpText = "Shortcut help";
+        Key = Key.F1;
+        return true;
+    }
 
-        if (HelpView?.IsAdded == false)
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        if (disposing)
         {
-            HelpView.Dispose ();
-        }
+            if (CommandView?.IsAdded == false)
+            {
+                CommandView.Dispose ();
+            }
 
-        if (KeyView?.IsAdded == false)
-        {
-            KeyView.Dispose ();
+            if (HelpView?.IsAdded == false)
+            {
+                HelpView.Dispose ();
+            }
+
+            if (KeyView?.IsAdded == false)
+            {
+                KeyView.Dispose ();
+            }
         }
-    }
 
-    base.Dispose (disposing);
-}
+        base.Dispose (disposing);
+    }
 }