소스 검색

Keyboard UI for move/resize POC

Tig 10 달 전
부모
커밋
85a97e6a05

+ 38 - 3
Terminal.Gui/Application/Application.Keyboard.cs

@@ -5,12 +5,10 @@ public static partial class Application // Keyboard handling
 {
 {
     private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrrides
     private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrrides
     private static Key _nextTabKey = Key.Tab; // Resources/config.json overrrides
     private static Key _nextTabKey = Key.Tab; // Resources/config.json overrrides
-
     private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrrides
     private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrrides
-
     private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrrides
     private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrrides
-
     private static Key _quitKey = Key.Esc; // Resources/config.json overrrides
     private static Key _quitKey = Key.Esc; // Resources/config.json overrrides
+    private static Key _arrangeKey = Key.F5.WithCtrl; // Resources/config.json overrrides
 
 
     static Application () { AddApplicationKeyBindings (); }
     static Application () { AddApplicationKeyBindings (); }
 
 
@@ -262,6 +260,22 @@ public static partial class Application // Keyboard handling
         }
         }
     }
     }
 
 
+
+    /// <summary>Gets or sets the key to activate arranging views using the keyboard.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key ArrangeKey
+    {
+        get => _arrangeKey;
+        set
+        {
+            if (_arrangeKey != value)
+            {
+                ReplaceKey (_arrangeKey, value);
+                _arrangeKey = value;
+            }
+        }
+    }
+
     internal static void AddApplicationKeyBindings ()
     internal static void AddApplicationKeyBindings ()
     {
     {
         CommandImplementations = new ();
         CommandImplementations = new ();
@@ -344,6 +358,24 @@ public static partial class Application // Keyboard handling
                     }
                     }
                    );
                    );
 
 
+        AddCommand (Command.Edit, static () =>
+                                  {
+                                      View? viewToArrange = Navigation?.GetFocused ();
+
+                                      // Go up the superview hierarchy and find the first that is not ViewArrangement.Fixed
+                                      while (viewToArrange?.SuperView is { } && viewToArrange.Arrangement == ViewArrangement.Fixed)
+                                      {
+                                          viewToArrange = viewToArrange.SuperView;
+                                      }
+
+                                      if (viewToArrange is { })
+                                      {
+                                          return viewToArrange.Border?.Arrange ();
+                                      }
+
+                                      return false;
+                                  });
+
         KeyBindings.Clear ();
         KeyBindings.Clear ();
 
 
         // Resources/config.json overrrides
         // Resources/config.json overrrides
@@ -352,6 +384,7 @@ public static partial class Application // Keyboard handling
         NextTabGroupKey = Key.F6;
         NextTabGroupKey = Key.F6;
         PrevTabGroupKey = Key.F6.WithShift;
         PrevTabGroupKey = Key.F6.WithShift;
         QuitKey = Key.Esc;
         QuitKey = Key.Esc;
+        ArrangeKey = Key.F5.WithCtrl;
 
 
         KeyBindings.Add (QuitKey, KeyBindingScope.Application, Command.QuitToplevel);
         KeyBindings.Add (QuitKey, KeyBindingScope.Application, Command.QuitToplevel);
 
 
@@ -365,6 +398,8 @@ public static partial class Application // Keyboard handling
         KeyBindings.Add (NextTabGroupKey, KeyBindingScope.Application, Command.NextViewOrTop);
         KeyBindings.Add (NextTabGroupKey, KeyBindingScope.Application, Command.NextViewOrTop);
         KeyBindings.Add (PrevTabGroupKey, KeyBindingScope.Application, Command.PreviousViewOrTop);
         KeyBindings.Add (PrevTabGroupKey, KeyBindingScope.Application, Command.PreviousViewOrTop);
 
 
+        KeyBindings.Add (ArrangeKey, KeyBindingScope.Application, Command.Edit);
+
         // TODO: Refresh Key should be configurable
         // TODO: Refresh Key should be configurable
         KeyBindings.Add (Key.F5, KeyBindingScope.Application, Command.Refresh);
         KeyBindings.Add (Key.F5, KeyBindingScope.Application, Command.Refresh);
 
 

+ 6 - 1
Terminal.Gui/Input/Command.cs

@@ -267,5 +267,10 @@ public enum Command
     New,
     New,
 
 
     /// <summary>Shows context about the item (e.g. a context menu).</summary>
     /// <summary>Shows context about the item (e.g. a context menu).</summary>
-    ShowContextMenu
+    ShowContextMenu,
+
+    /// <summary>
+    /// Invokes a user interface for editing.
+    /// </summary>
+    Edit
 }
 }

+ 1 - 0
Terminal.Gui/Resources/config.json

@@ -22,6 +22,7 @@
   "Application.NextTabGroupKey": "F6",
   "Application.NextTabGroupKey": "F6",
   "Application.PrevTabGroupKey": "Shift+F6",
   "Application.PrevTabGroupKey": "Shift+F6",
   "Application.QuitKey": "Esc",
   "Application.QuitKey": "Esc",
+  "Application.ArrangeKey":  "Ctrl+F5",
   "Key.Separator": "+",
   "Key.Separator": "+",
 
 
   "Theme": "Default",
   "Theme": "Default",

+ 193 - 12
Terminal.Gui/View/Adornment/Border.cs

@@ -1,3 +1,6 @@
+using System.Diagnostics;
+using static Terminal.Gui.SpinnerStyle;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>The Border for a <see cref="View"/>.</summary>
 /// <summary>The Border for a <see cref="View"/>.</summary>
@@ -52,8 +55,9 @@ public class Border : Adornment
     /// <inheritdoc/>
     /// <inheritdoc/>
     public Border (View parent) : base (parent)
     public Border (View parent) : base (parent)
     {
     {
-        /* Do nothing; View.CreateAdornment requires a constructor that takes a parent */
         Parent = parent;
         Parent = parent;
+        CanFocus = false;
+
         Application.GrabbingMouse += Application_GrabbingMouse;
         Application.GrabbingMouse += Application_GrabbingMouse;
         Application.UnGrabbingMouse += Application_UnGrabbingMouse;
         Application.UnGrabbingMouse += Application_UnGrabbingMouse;
 
 
@@ -70,6 +74,10 @@ public class Border : Adornment
     public Button CloseButton { get; internal set; }
     public Button CloseButton { get; internal set; }
 #endif
 #endif
 
 
+
+    [CanBeNull]
+    private Button _arrangeButton;
+
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override void BeginInit ()
     public override void BeginInit ()
     {
     {
@@ -108,8 +116,11 @@ public class Border : Adornment
             LayoutStarted += OnLayoutStarted;
             LayoutStarted += OnLayoutStarted;
     }
     }
 #endif
 #endif
+        
     }
     }
 
 
+
+
 #if SUBVIEW_BASED_BORDER
 #if SUBVIEW_BASED_BORDER
     private void OnLayoutStarted (object sender, LayoutEventArgs e)
     private void OnLayoutStarted (object sender, LayoutEventArgs e)
     {
     {
@@ -427,6 +438,8 @@ public class Border : Adornment
         int sideLineLength = borderBounds.Height;
         int sideLineLength = borderBounds.Height;
         bool canDrawBorder = borderBounds is { Width: > 0, Height: > 0 };
         bool canDrawBorder = borderBounds is { Width: > 0, Height: > 0 };
 
 
+        LineStyle lineStyle = LineStyle;
+
         if (Settings.FastHasFlags (BorderSettings.Title))
         if (Settings.FastHasFlags (BorderSettings.Title))
         {
         {
             if (Thickness.Top == 2)
             if (Thickness.Top == 2)
@@ -506,7 +519,7 @@ public class Border : Adornment
                                 new (borderBounds.Location.X, titleY),
                                 new (borderBounds.Location.X, titleY),
                                 borderBounds.Width,
                                 borderBounds.Width,
                                 Orientation.Horizontal,
                                 Orientation.Horizontal,
-                                LineStyle,
+                                lineStyle,
                                 Driver.GetAttribute ()
                                 Driver.GetAttribute ()
                                );
                                );
                 }
                 }
@@ -521,7 +534,7 @@ public class Border : Adornment
                                     new (borderBounds.X + 1, topTitleLineY),
                                     new (borderBounds.X + 1, topTitleLineY),
                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                     Orientation.Horizontal,
                                     Orientation.Horizontal,
-                                    LineStyle,
+                                    lineStyle,
                                     Driver.GetAttribute ()
                                     Driver.GetAttribute ()
                                    );
                                    );
                     }
                     }
@@ -535,7 +548,7 @@ public class Border : Adornment
                                     new (borderBounds.X + 1, topTitleLineY),
                                     new (borderBounds.X + 1, topTitleLineY),
                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                     Orientation.Horizontal,
                                     Orientation.Horizontal,
-                                    LineStyle,
+                                    lineStyle,
                                     Driver.GetAttribute ()
                                     Driver.GetAttribute ()
                                    );
                                    );
 
 
@@ -543,7 +556,7 @@ public class Border : Adornment
                                     new (borderBounds.X + 1, topTitleLineY + 2),
                                     new (borderBounds.X + 1, topTitleLineY + 2),
                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                     Orientation.Horizontal,
                                     Orientation.Horizontal,
-                                    LineStyle,
+                                    lineStyle,
                                     Driver.GetAttribute ()
                                     Driver.GetAttribute ()
                                    );
                                    );
                     }
                     }
@@ -554,7 +567,7 @@ public class Border : Adornment
                                 new (borderBounds.Location.X, titleY),
                                 new (borderBounds.Location.X, titleY),
                                 2,
                                 2,
                                 Orientation.Horizontal,
                                 Orientation.Horizontal,
-                                LineStyle,
+                                lineStyle,
                                 Driver.GetAttribute ()
                                 Driver.GetAttribute ()
                                );
                                );
 
 
@@ -593,7 +606,7 @@ public class Border : Adornment
                                     ),
                                     ),
                                 borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                 borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                 Orientation.Horizontal,
                                 Orientation.Horizontal,
-                                LineStyle,
+                                lineStyle,
                                 Driver.GetAttribute ()
                                 Driver.GetAttribute ()
                                );
                                );
                 }
                 }
@@ -607,7 +620,7 @@ public class Border : Adornment
                             new (borderBounds.Location.X, titleY),
                             new (borderBounds.Location.X, titleY),
                             sideLineLength,
                             sideLineLength,
                             Orientation.Vertical,
                             Orientation.Vertical,
-                            LineStyle,
+                            lineStyle,
                             Driver.GetAttribute ()
                             Driver.GetAttribute ()
                            );
                            );
             }
             }
@@ -619,7 +632,7 @@ public class Border : Adornment
                             new (borderBounds.X, borderBounds.Y + borderBounds.Height - 1),
                             new (borderBounds.X, borderBounds.Y + borderBounds.Height - 1),
                             borderBounds.Width,
                             borderBounds.Width,
                             Orientation.Horizontal,
                             Orientation.Horizontal,
-                            LineStyle,
+                            lineStyle,
                             Driver.GetAttribute ()
                             Driver.GetAttribute ()
                            );
                            );
             }
             }
@@ -630,7 +643,7 @@ public class Border : Adornment
                             new (borderBounds.X + borderBounds.Width - 1, titleY),
                             new (borderBounds.X + borderBounds.Width - 1, titleY),
                             sideLineLength,
                             sideLineLength,
                             Orientation.Vertical,
                             Orientation.Vertical,
-                            LineStyle,
+                            lineStyle,
                             Driver.GetAttribute ()
                             Driver.GetAttribute ()
                            );
                            );
             }
             }
@@ -705,7 +718,7 @@ public class Border : Adornment
     private static void GetAppealingGradientColors (out List<Color> stops, out List<int> steps)
     private static void GetAppealingGradientColors (out List<Color> stops, out List<int> steps)
     {
     {
         // Define the colors of the gradient stops with more appealing colors
         // Define the colors of the gradient stops with more appealing colors
-        stops = new()
+        stops = new ()
         {
         {
             new (0, 128, 255), // Bright Blue
             new (0, 128, 255), // Bright Blue
             new (0, 255, 128), // Bright Green
             new (0, 255, 128), // Bright Green
@@ -716,6 +729,174 @@ public class Border : Adornment
 
 
         // Define the number of steps between each color for smoother transitions
         // Define the number of steps between each color for smoother transitions
         // If we pass only a single value then it will assume equal steps between all pairs
         // If we pass only a single value then it will assume equal steps between all pairs
-        steps = new() { 15 };
+        steps = new () { 15 };
+    }
+
+    private ViewArrangement _arranging;
+
+    public bool? Arrange ()
+    {
+        Debug.Assert (_arranging == ViewArrangement.Fixed);
+
+        CanFocus = true;
+        SetFocus ();
+
+        Debug.Assert (_arrangeButton is null);
+        _arrangeButton = new Button
+        {
+            CanFocus = true,
+            Width = 1,
+            Height = 1,
+            NoDecorations = true,
+            NoPadding = true,
+            ShadowStyle = ShadowStyle.None,
+            Text = $"{Glyphs.Diamond}",
+        };
+        Add (_arrangeButton);
+
+        AddCommand (Command.QuitToplevel, EndArrange);
+
+        AddCommand (Command.LineUp,
+                    () =>
+                    {
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.Y = Parent.Y - 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            if (Parent!.Viewport.Height > 0)
+                            {
+                                Parent!.Height = Parent.Height - 1;
+                            }
+                        }
+
+                        return true;
+                    });
+
+        AddCommand (Command.LineDown,
+                    () =>
+                    {
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.Y = Parent.Y + 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            Parent!.Height = Parent.Height + 1;
+                        }
+
+                        return true;
+                    });
+        AddCommand (Command.Left,
+                    () =>
+                    {
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.X = Parent.X - 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            if (Parent!.Viewport.Width > 0)
+                            {
+                                Parent!.Width = Parent.Width - 1;
+                            }
+                        }
+                        return true;
+                    });
+
+        AddCommand (Command.Right,
+                    () =>
+                    {
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.X = Parent.X + 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            Parent!.Width = Parent.Width + 1;
+                        }
+
+                        return true;
+                    });
+
+        AddCommand (Command.Tab,
+                    () =>
+                    {
+                        // TODO: Move arrangement focus to next side
+                        if (Parent!.Arrangement.HasFlag (ViewArrangement.Resizable))
+                        {
+                            _arranging = ViewArrangement.Resizable;
+                            _arrangeButton.X = Pos.AnchorEnd ();
+                            _arrangeButton.Y = Pos.AnchorEnd ();
+
+                            return true;
+                        }
+                        return true;
+                    });
+
+        AddCommand (Command.BackTab,
+                    () =>
+                    {
+                        // TODO: Move arrangement focus to prev side
+                        if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
+                        {
+                            _arranging = ViewArrangement.Movable;
+                            _arrangeButton.X = 0;
+                            _arrangeButton.Y = 0;
+                            return true;
+                        }
+                        return true;
+                    });
+
+        KeyBindings.Add (Key.Esc, KeyBindingScope.HotKey, Command.QuitToplevel);
+        KeyBindings.Add (Application.ArrangeKey, KeyBindingScope.HotKey, Command.QuitToplevel);
+        KeyBindings.Add (Key.CursorUp, KeyBindingScope.HotKey, Command.LineUp);
+        KeyBindings.Add (Key.CursorDown, KeyBindingScope.HotKey, Command.LineDown);
+        KeyBindings.Add (Key.CursorLeft, KeyBindingScope.HotKey, Command.Left);
+        KeyBindings.Add (Key.CursorRight, KeyBindingScope.HotKey, Command.Right);
+
+        KeyBindings.Add (Key.Tab, KeyBindingScope.HotKey, Command.Tab);
+        KeyBindings.Add (Key.Tab.WithShift, KeyBindingScope.HotKey, Command.BackTab);
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
+        {
+            _arranging = ViewArrangement.Movable;
+            _arrangeButton.X = 0;
+            _arrangeButton.Y = 0;
+            return true;
+        }
+        else
+        {
+            if (Parent!.Arrangement.HasFlag (ViewArrangement.Resizable))
+            {
+                _arranging = ViewArrangement.Resizable;
+                _arrangeButton.X = Pos.AnchorEnd ();
+                _arrangeButton.Y = Pos.AnchorEnd ();
+
+                return true;
+            }
+        }
+
+        // Hack for now
+        EndArrange ();
+        return false;
+    }
+    private bool? EndArrange ()
+    {
+        _arranging = ViewArrangement.Fixed;
+        CanFocus = false;
+
+        KeyBindings.Clear ();
+
+        Remove (_arrangeButton);
+        _arrangeButton.Dispose ();
+        _arrangeButton = null;
+
+        return true;
     }
     }
 }
 }

+ 1 - 1
Terminal.Gui/View/Adornment/ShadowView.cs

@@ -137,7 +137,7 @@ internal class ShadowView : View
         Rectangle screen = ViewportToScreen (viewport);
         Rectangle screen = ViewportToScreen (viewport);
 
 
         // Fill the rest of the rectangle
         // Fill the rest of the rectangle
-        for (int i = screen.Y; i < screen.Y + viewport.Height; i++)
+        for (int i = Math.Max (0, screen.Y); i < screen.Y + viewport.Height; i++)
         {
         {
             Driver.Move (screen.X, i);
             Driver.Move (screen.X, i);
 
 

+ 7 - 0
Terminal.Gui/View/View.Keyboard.cs

@@ -579,6 +579,13 @@ public partial class View // Keyboard APIs
 
 
     private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, KeyBindingScope scope, ref bool? handled)
     private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, KeyBindingScope scope, ref bool? handled)
     {
     {
+        bool? adornmentHandled = adornment.OnInvokingKeyBindings (keyEvent, scope);
+
+        if (adornmentHandled is true)
+        {
+            return true;
+        }
+
         if (adornment?.Subviews is null)
         if (adornment?.Subviews is null)
         {
         {
             return false;
             return false;

+ 23 - 0
Terminal.Gui/View/View.Navigation.cs

@@ -422,6 +422,20 @@ public partial class View // Focus and cross-view navigation management (TabStop
             }
             }
         }
         }
 
 
+        // Are we an Adornment? 
+        if (this is Adornment adornment)
+        {
+            if (adornment.Parent is { HasFocus: false } parent)
+            {
+                (bool focusSet, bool parentCancelled) = parent.SetHasFocusTrue (previousFocusedView, true);
+
+                if (!focusSet)
+                {
+                    return (false, parentCancelled);
+                }
+            }
+        }
+
         if (_hasFocus)
         if (_hasFocus)
         {
         {
             // Something else beat us to the change (likely a FocusChanged handler).
             // Something else beat us to the change (likely a FocusChanged handler).
@@ -433,6 +447,15 @@ public partial class View // Focus and cross-view navigation management (TabStop
         // Get whatever peer has focus, if any
         // Get whatever peer has focus, if any
         View? focusedPeer = SuperView?.Focused;
         View? focusedPeer = SuperView?.Focused;
 
 
+        if (focusedPeer is null)
+        {
+            // Are we an Adornment? 
+            if (this is Adornment ad)
+            {
+                focusedPeer = ad.Parent?.Focused;
+            }
+        }
+
         _hasFocus = true;
         _hasFocus = true;
 
 
         // Ensure that the peer loses focus
         // Ensure that the peer loses focus

+ 0 - 11
Terminal.Gui/Views/Dialog.cs

@@ -80,17 +80,6 @@ public class Dialog : Window
         Modal = true;
         Modal = true;
         ButtonAlignment = DefaultButtonAlignment;
         ButtonAlignment = DefaultButtonAlignment;
         ButtonAlignmentModes = DefaultButtonAlignmentModes;
         ButtonAlignmentModes = DefaultButtonAlignmentModes;
-
-        AddCommand (
-                    Command.QuitToplevel,
-                    () =>
-                    {
-                        Canceled = true;
-                        RequestStop ();
-
-                        return true;
-                    });
-        KeyBindings.Add (Key.Esc, Command.QuitToplevel);
     }
     }
 
 
     // BUGBUG: We override GetNormal/FocusColor because "Dialog" ColorScheme is goofy.
     // BUGBUG: We override GetNormal/FocusColor because "Dialog" ColorScheme is goofy.