diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs
index 39d98f635..1ed64abca 100644
--- a/Terminal.Gui/View/Adornment/Border.cs
+++ b/Terminal.Gui/View/Adornment/Border.cs
@@ -1,3 +1,5 @@
+using Terminal.Gui.Drawing;
+
namespace Terminal.Gui;
/// The Border for a .
@@ -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)
+ )
+ )
+ );
}
+
///
/// Sets the style of the border by changing the . This is a helper API for setting the
/// to (1,1,1,1) 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;
///
- /// Gets or sets whether the title should be shown. The default is .
+ /// Gets or sets the settings for the border.
///
- 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;
- ///
+ ///
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;
}
-
///
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 stops, out List 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 stops, out List 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 };
+ }
}
diff --git a/Terminal.Gui/View/Adornment/BorderSettings.cs b/Terminal.Gui/View/Adornment/BorderSettings.cs
new file mode 100644
index 000000000..5829d1ed6
--- /dev/null
+++ b/Terminal.Gui/View/Adornment/BorderSettings.cs
@@ -0,0 +1,26 @@
+using Terminal.Gui.Analyzers.Internal.Attributes;
+
+namespace Terminal.Gui;
+
+///
+/// Determines the settings for .
+///
+[Flags]
+[GenerateEnumExtensionMethods (FastHasFlags = true)]
+public enum BorderSettings
+{
+ ///
+ /// No settings.
+ ///
+ None = 0,
+
+ ///
+ /// Show the title.
+ ///
+ Title = 1,
+
+ ///
+ /// Use to draw the border.
+ ///
+ Gradient = 2,
+}
diff --git a/Terminal.Gui/View/Adornment/Margin.cs b/Terminal.Gui/View/Adornment/Margin.cs
index 2e1ea5760..046965e32 100644
--- a/Terminal.Gui/View/Adornment/Margin.cs
+++ b/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;
}
diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs
index 802631d52..7ddbe7c5a 100644
--- a/Terminal.Gui/Views/Shortcut.cs
+++ b/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 ();
diff --git a/UICatalog/Scenarios/BorderEditor.cs b/UICatalog/Scenarios/BorderEditor.cs
index a5ccae212..5e0a6e77a 100644
--- a/UICatalog/Scenarios/BorderEditor.cs
+++ b/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 borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast ().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 args) { ((Border)AdornmentToEdit).ShowTitle = args.NewValue == CheckState.Checked; }
+ void OnCkbTitleOnToggle (object sender, CancelEventArgs args)
+ {
+ if (args.NewValue == CheckState.Checked)
+
+ {
+ ((Border)AdornmentToEdit).Settings |= BorderSettings.Title;
+ }
+ else
+
+ {
+ ((Border)AdornmentToEdit).Settings &= ~BorderSettings.Title;
+ }
+ }
+
+ void OnCkbGradientOnToggle (object sender, CancelEventArgs args)
+ {
+ if (args.NewValue == CheckState.Checked)
+
+ {
+ ((Border)AdornmentToEdit).Settings |= BorderSettings.Gradient;
+ }
+ else
+
+ {
+ ((Border)AdornmentToEdit).Settings &= ~BorderSettings.Gradient;
+ }
+ }
}
-}
\ No newline at end of file
+}