소스 검색

Added Border Settings.
Enabled BorderSettings.Gradient

Tig 1 년 전
부모
커밋
65efddbc9f

+ 96 - 50
Terminal.Gui/View/Adornment/Border.cs

@@ -1,3 +1,5 @@
+using Terminal.Gui.Drawing;
+
 namespace Terminal.Gui;
 
 /// <summary>The Border for a <see cref="View"/>.</summary>
@@ -78,7 +80,7 @@ public class Border : Adornment
         if ((Parent?.Arrangement & ViewArrangement.Movable) != 0)
         {
             HighlightStyle |= HighlightStyle.Hover;
-        }   
+        }
 #endif
 
         base.BeginInit ();
@@ -149,31 +151,32 @@ public class Border : Adornment
         }
     }
 
-    Rectangle GetBorderRectangle (Rectangle screenRect)
+    private Rectangle GetBorderRectangle (Rectangle screenRect)
     {
         return new (
-                                      screenRect.X + Math.Max (0, Thickness.Left - 1),
-                                      screenRect.Y + Math.Max (0, Thickness.Top - 1),
-                                      Math.Max (
-                                                0,
-                                                screenRect.Width
-                                                - Math.Max (
-                                                            0,
-                                                            Math.Max (0, Thickness.Left - 1)
-                                                            + Math.Max (0, Thickness.Right - 1)
-                                                           )
-                                               ),
-                                      Math.Max (
-                                                0,
-                                                screenRect.Height
-                                                - Math.Max (
-                                                            0,
-                                                            Math.Max (0, Thickness.Top - 1)
-                                                            + Math.Max (0, Thickness.Bottom - 1)
-                                                           )
-                                               )
-                                     );
+                    screenRect.X + Math.Max (0, Thickness.Left - 1),
+                    screenRect.Y + Math.Max (0, Thickness.Top - 1),
+                    Math.Max (
+                              0,
+                              screenRect.Width
+                              - Math.Max (
+                                          0,
+                                          Math.Max (0, Thickness.Left - 1)
+                                          + Math.Max (0, Thickness.Right - 1)
+                                         )
+                             ),
+                    Math.Max (
+                              0,
+                              screenRect.Height
+                              - Math.Max (
+                                          0,
+                                          Math.Max (0, Thickness.Top - 1)
+                                          + Math.Max (0, Thickness.Bottom - 1)
+                                         )
+                             )
+                   );
     }
+
     /// <summary>
     ///     Sets the style of the border by changing the <see cref="Thickness"/>. This is a helper API for setting the
     ///     <see cref="Thickness"/> to <c>(1,1,1,1)</c> and setting the line style of the views that comprise the border. If
@@ -196,21 +199,22 @@ public class Border : Adornment
         set => _lineStyle = value;
     }
 
-    private bool _showTitle = true;
+    private BorderSettings _settings = BorderSettings.Title;
 
     /// <summary>
-    ///     Gets or sets whether the title should be shown. The default is <see langword="true"/>.
+    ///     Gets or sets the settings for the border.
     /// </summary>
-    public bool ShowTitle
+    public BorderSettings Settings
     {
-        get => _showTitle;
+        get => _settings;
         set
         {
-            if (value == _showTitle)
+            if (value == _settings)
             {
                 return;
             }
-            _showTitle = value;
+
+            _settings = value;
 
             Parent?.SetNeedsDisplay ();
         }
@@ -225,6 +229,7 @@ public class Border : Adornment
         if (!Parent.Arrangement.HasFlag (ViewArrangement.Movable))
         {
             e.Cancel = true;
+
             return;
         }
 
@@ -235,9 +240,9 @@ public class Border : Adornment
                 _savedForeColor = ColorScheme.Normal.Foreground;
             }
 
-            ColorScheme cs = new ColorScheme (ColorScheme)
+            var cs = new ColorScheme (ColorScheme)
             {
-                Normal = new Attribute (ColorScheme.Normal.Foreground.GetHighlightColor (), ColorScheme.Normal.Background)
+                Normal = new (ColorScheme.Normal.Foreground.GetHighlightColor (), ColorScheme.Normal.Background)
             };
             ColorScheme = cs;
         }
@@ -254,12 +259,13 @@ public class Border : Adornment
 
         if (e.NewValue == HighlightStyle.None && _savedForeColor.HasValue)
         {
-            ColorScheme cs = new ColorScheme (ColorScheme)
+            var cs = new ColorScheme (ColorScheme)
             {
-                Normal = new Attribute (_savedForeColor.Value, ColorScheme.Normal.Background)
+                Normal = new (_savedForeColor.Value, ColorScheme.Normal.Background)
             };
             ColorScheme = cs;
         }
+
         Parent?.SetNeedsDisplay ();
         e.Cancel = true;
     }
@@ -267,7 +273,7 @@ public class Border : Adornment
     private Point? _dragPosition;
     private Point _startGrabPoint;
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
     {
         if (base.OnMouseEvent (mouseEvent))
@@ -322,16 +328,17 @@ public class Border : Adornment
 
                 _dragPosition = mouseEvent.Position;
 
-                Point parentLoc = Parent.SuperView?.ScreenToViewport (new (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y)) ?? mouseEvent.ScreenPosition;
+                Point parentLoc = Parent.SuperView?.ScreenToViewport (new (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y))
+                                  ?? mouseEvent.ScreenPosition;
 
                 GetLocationEnsuringFullVisibility (
-                                     Parent,
-                                     parentLoc.X - _startGrabPoint.X,
-                                     parentLoc.Y - _startGrabPoint.Y,
-                                     out int nx,
-                                     out int ny,
-                                     out _
-                                    );
+                                                   Parent,
+                                                   parentLoc.X - _startGrabPoint.X,
+                                                   parentLoc.Y - _startGrabPoint.Y,
+                                                   out int nx,
+                                                   out int ny,
+                                                   out _
+                                                  );
 
                 Parent.X = nx;
                 Parent.Y = ny;
@@ -352,7 +359,6 @@ public class Border : Adornment
         return false;
     }
 
-
     /// <inheritdoc/>
     protected override void Dispose (bool disposing)
     {
@@ -403,7 +409,7 @@ public class Border : Adornment
         // ...thickness extends outward (border/title is always as far in as possible)
         // PERF: How about a call to Rectangle.Offset?
 
-        var borderBounds = GetBorderRectangle (screenBounds);
+        Rectangle borderBounds = GetBorderRectangle (screenBounds);
         int topTitleLineY = borderBounds.Y;
         int titleY = borderBounds.Y;
         var titleBarsLength = 0; // the little vertical thingies
@@ -421,7 +427,7 @@ public class Border : Adornment
         int sideLineLength = borderBounds.Height;
         bool canDrawBorder = borderBounds is { Width: > 0, Height: > 0 };
 
-        if (ShowTitle)
+        if (Settings.FastHasFlags (BorderSettings.Title))
         {
             if (Thickness.Top == 2)
             {
@@ -453,9 +459,10 @@ public class Border : Adornment
             }
         }
 
-        if (canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && ShowTitle && !string.IsNullOrEmpty (Parent?.Title))
+        if (canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && Settings.FastHasFlags (BorderSettings.Title) && !string.IsNullOrEmpty (Parent?.Title))
         {
-            var focus = Parent.GetNormalColor ();
+            Attribute focus = Parent.GetNormalColor ();
+
             if (Parent.SuperView is { } && Parent.SuperView?.Subviews!.Count (s => s.CanFocus) > 1)
             {
                 // Only use focus color if there are multiple focusable views
@@ -492,7 +499,7 @@ public class Border : Adornment
             {
                 // ╔╡Title╞═════╗
                 // ╔╡╞═════╗
-                if (borderBounds.Width < 4 || !ShowTitle || string.IsNullOrEmpty (Parent?.Title))
+                if (borderBounds.Width < 4 || !Settings.FastHasFlags (BorderSettings.Title) || string.IsNullOrEmpty (Parent?.Title))
                 {
                     // ╔╡╞╗ should be ╔══╗
                     lc.AddLine (
@@ -631,7 +638,7 @@ public class Border : Adornment
             Driver.SetAttribute (prevAttr);
 
             // TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler
-            if (View.Diagnostics.HasFlag (ViewDiagnosticFlags.Ruler))
+            if (Diagnostics.HasFlag (ViewDiagnosticFlags.Ruler))
             {
                 // Top
                 var hruler = new Ruler { Length = screenBounds.Width, Orientation = Orientation.Horizontal };
@@ -642,7 +649,7 @@ public class Border : Adornment
                 }
 
                 // Redraw title 
-                if (drawTop && maxTitleWidth > 0 && ShowTitle)
+                if (drawTop && maxTitleWidth > 0 && Settings.FastHasFlags (BorderSettings.Title))
                 {
                     Parent.TitleTextFormatter.Draw (
                                                     new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
@@ -670,6 +677,45 @@ public class Border : Adornment
                     vruler.Draw (new (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1);
                 }
             }
+
+            // TODO: This should not be done on each draw?
+            if (Settings.FastHasFlags (BorderSettings.Gradient))
+            {
+                SetupGradientLineCanvas (lc, screenBounds);
+            }
+            else
+            {
+                lc.Fill = null;
+            }
         }
     }
+
+    private void SetupGradientLineCanvas (LineCanvas lc, Rectangle rect)
+    {
+        GetAppealingGradientColors (out List<Color> stops, out List<int> steps);
+
+        var g = new Gradient (stops, steps);
+
+        var fore = new GradientFill (rect, g, GradientDirection.Diagonal);
+        var back = new SolidFill (GetNormalColor ().Background);
+
+        lc.Fill = new (fore, back);
+    }
+
+    private static void GetAppealingGradientColors (out List<Color> stops, out List<int> steps)
+    {
+        // Define the colors of the gradient stops with more appealing colors
+        stops = new()
+        {
+            new (0, 128, 255), // Bright Blue
+            new (0, 255, 128), // Bright Green
+            new (255, 255), // Bright Yellow
+            new (255, 128), // Bright Orange
+            new (255, 0, 128) // Bright Pink
+        };
+
+        // 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
+        steps = new() { 15 };
+    }
 }

+ 26 - 0
Terminal.Gui/View/Adornment/BorderSettings.cs

@@ -0,0 +1,26 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui;
+
+/// <summary>
+/// Determines the settings for <see cref="Border"/>.
+/// </summary>
+[Flags]
+[GenerateEnumExtensionMethods (FastHasFlags = true)]
+public enum BorderSettings
+{
+    /// <summary>
+    /// No settings.
+    /// </summary>
+    None = 0,
+
+    /// <summary>
+    /// Show the title.
+    /// </summary>
+    Title = 1,
+
+    /// <summary>
+    /// Use <see cref="GradientFill"/> to draw the border.
+    /// </summary>
+    Gradient = 2,
+}

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

@@ -223,7 +223,7 @@ public class Margin : Adornment
         if (ShadowStyle != ShadowStyle.None && _rightShadow is { } && _bottomShadow is { })
         {
             _rightShadow.Y = Parent.Border.Thickness.Top > 0
-                                 ? Parent.Border.Thickness.Top - (Parent.Border.Thickness.Top > 2 && Parent.Border.ShowTitle ? 1 : 0)
+                                 ? Parent.Border.Thickness.Top - (Parent.Border.Thickness.Top > 2 && Parent.Border.Settings.FastHasFlags (BorderSettings.Title) ? 1 : 0)
                                  : 1;
             _bottomShadow.X = Parent.Border.Thickness.Left > 0 ? Parent.Border.Thickness.Left : 1;
         }

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

@@ -104,7 +104,7 @@ public class Shortcut : View
         void OnInitialized (object sender, EventArgs e)
         {
             SuperViewRendersLineCanvas = true;
-            Border.ShowTitle = false;
+            Border.Settings &= ~BorderSettings.Title;
 
             ShowHide ();
 

+ 49 - 9
UICatalog/Scenarios/BorderEditor.cs

@@ -9,29 +9,30 @@ public class BorderEditor : AdornmentEditor
 {
     private CheckBox _ckbTitle;
     private RadioGroup _rbBorderStyle;
+    private CheckBox _ckbGradient;
 
     public BorderEditor ()
     {
         Title = "_Border";
         Initialized += BorderEditor_Initialized;
         AdornmentChanged += BorderEditor_AdornmentChanged;
-
     }
 
     private void BorderEditor_AdornmentChanged (object sender, EventArgs e)
     {
-        _ckbTitle.State = ((Border)AdornmentToEdit).ShowTitle ? CheckState.Checked : CheckState.UnChecked;
+        _ckbTitle.State = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Title) ? CheckState.Checked : CheckState.UnChecked;
         _rbBorderStyle.SelectedItem = (int)((Border)AdornmentToEdit).LineStyle;
+        _ckbGradient.State = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Gradient) ? CheckState.Checked : CheckState.UnChecked;
     }
 
     private void BorderEditor_Initialized (object sender, EventArgs e)
     {
-
         List<LineStyle> borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast<LineStyle> ().ToList ();
 
-        _rbBorderStyle = new RadioGroup
+        _rbBorderStyle = new()
         {
             X = 0,
+
             // BUGBUG: Hack until dimauto is working properly
             Y = Pos.Bottom (Subviews [^1]),
             Width = Dim.Width (Subviews [^2]) + Dim.Width (Subviews [^1]) - 1,
@@ -46,21 +47,34 @@ public class BorderEditor : AdornmentEditor
 
         _rbBorderStyle.SelectedItemChanged += OnRbBorderStyleOnSelectedItemChanged;
 
-        _ckbTitle = new CheckBox
+        _ckbTitle = new()
         {
             X = 0,
             Y = Pos.Bottom (_rbBorderStyle),
 
             State = CheckState.Checked,
             SuperViewRendersLineCanvas = true,
-            Text = "Show Title",
+            Text = "Title",
             Enabled = AdornmentToEdit is { }
         };
 
-
         _ckbTitle.Toggle += OnCkbTitleOnToggle;
         Add (_ckbTitle);
 
+        _ckbGradient = new ()
+        {
+            X = 0,
+            Y = Pos.Bottom (_ckbTitle),
+
+            State = CheckState.Checked,
+            SuperViewRendersLineCanvas = true,
+            Text = "Gradient",
+            Enabled = AdornmentToEdit is { }
+        };
+
+        _ckbGradient.Toggle += OnCkbGradientOnToggle;
+        Add (_ckbGradient);
+
         return;
 
         void OnRbBorderStyleOnSelectedItemChanged (object s, SelectedItemChangedArgs e)
@@ -81,6 +95,32 @@ public class BorderEditor : AdornmentEditor
             LayoutSubviews ();
         }
 
-        void OnCkbTitleOnToggle (object sender, CancelEventArgs<CheckState> args) { ((Border)AdornmentToEdit).ShowTitle = args.NewValue == CheckState.Checked; }
+        void OnCkbTitleOnToggle (object sender, CancelEventArgs<CheckState> args)
+        {
+            if (args.NewValue == CheckState.Checked)
+
+            {
+                ((Border)AdornmentToEdit).Settings |= BorderSettings.Title;
+            }
+            else
+
+            {
+                ((Border)AdornmentToEdit).Settings &= ~BorderSettings.Title;
+            }
+        }
+
+        void OnCkbGradientOnToggle (object sender, CancelEventArgs<CheckState> args)
+        {
+            if (args.NewValue == CheckState.Checked)
+
+            {
+                ((Border)AdornmentToEdit).Settings |= BorderSettings.Gradient;
+            }
+            else
+
+            {
+                ((Border)AdornmentToEdit).Settings &= ~BorderSettings.Gradient;
+            }
+        }
     }
-}
+}