diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs
index 15b2dc666..837881b44 100644
--- a/Terminal.Gui/Application.cs
+++ b/Terminal.Gui/Application.cs
@@ -1227,7 +1227,7 @@ public static partial class Application {
}
}
- bool FrameHandledMouseEvent (Frame frame)
+ bool FrameHandledMouseEvent (Adornment frame)
{
if (frame?.Thickness.Contains (frame.FrameToScreen (), a.MouseEvent.X, a.MouseEvent.Y) ?? false) {
var boundsPoint = frame.ScreenToBounds (a.MouseEvent.X, a.MouseEvent.Y);
diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
index 08cc4a9c7..5f88a09e1 100644
--- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
@@ -535,13 +535,13 @@ public abstract class ConsoleDriver {
Off = 0b_0000_0000,
///
- /// When enabled, will draw a
+ /// When enabled, will draw a
/// ruler in the frame for any side with a padding value greater than 0.
///
FrameRuler = 0b_0000_0001,
///
- /// When enabled, will draw a
+ /// When enabled, will draw a
/// 'L', 'R', 'T', and 'B' when clearing 's instead of ' '.
///
FramePadding = 0b_0000_0010
diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
index 74a57dd80..81d9b0d21 100644
--- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
@@ -43,6 +43,8 @@ public class FakeDriver : ConsoleDriver {
public FakeDriver ()
{
+ Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH;
+ Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT;
if (FakeBehaviors.UseFakeClipboard) {
Clipboard = new FakeClipboard (FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse);
} else {
diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
index ed9f004c7..939c4ee4c 100644
--- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
@@ -825,7 +825,8 @@ internal class WindowsDriver : ConsoleDriver {
// TODO: if some other Windows-based terminal supports true color, update this logic to not
// force 16color mode (.e.g ConEmu which really doesn't work well at all).
- _isWindowsTerminal = Environment.GetEnvironmentVariable ("WT_SESSION") != null;
+ _isWindowsTerminal = _isWindowsTerminal = Environment.GetEnvironmentVariable ("WT_SESSION") != null ||
+ Environment.GetEnvironmentVariable ("VSAPPIDNAME") != null;
if (!_isWindowsTerminal) {
Force16Colors = true;
}
diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs
index 297726949..0fbbd0e33 100644
--- a/Terminal.Gui/Drawing/Cell.cs
+++ b/Terminal.Gui/Drawing/Cell.cs
@@ -40,4 +40,7 @@ public class Cell {
/// been modified since the last time it was drawn.
///
public bool IsDirty { get; set; }
+
+ ///
+ public override string ToString () => $"[{Rune}, {Attribute}]";
}
diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs
index 65ded54fa..f4c221ddd 100644
--- a/Terminal.Gui/Drawing/Color.cs
+++ b/Terminal.Gui/Drawing/Color.cs
@@ -730,7 +730,6 @@ public readonly struct Attribute : IEquatable {
///
/// Default empty attribute.
///
- ///
public static readonly Attribute Default = new (Color.White, Color.Black);
///
@@ -890,7 +889,7 @@ public readonly struct Attribute : IEquatable {
///
public override string ToString () =>
// Note, Unit tests are dependent on this format
- $"{Foreground},{Background}";
+ $"[{Foreground},{Background}]";
}
///
@@ -966,7 +965,7 @@ public class ColorScheme : IEquatable {
}
///
- /// The foreground and background color for text when the view is highlighted (hot).
+ /// The foreground and background color for text in a non-focused view that indicates a .
///
public Attribute HotNormal {
get => _hotNormal;
@@ -974,7 +973,7 @@ public class ColorScheme : IEquatable {
}
///
- /// The foreground and background color for text when the view is highlighted (hot) and has focus.
+ /// The foreground and background color for for text in a focused view that indicates a .
///
public Attribute HotFocus {
get => _hotFocus;
@@ -982,7 +981,7 @@ public class ColorScheme : IEquatable {
}
///
- /// The default foreground and background color for text, when the view is disabled.
+ /// The default foreground and background color for text when the view is disabled.
///
public Attribute Disabled {
get => _disabled;
diff --git a/Terminal.Gui/View/Adornment/Adornment.cs b/Terminal.Gui/View/Adornment/Adornment.cs
new file mode 100644
index 000000000..599291001
--- /dev/null
+++ b/Terminal.Gui/View/Adornment/Adornment.cs
@@ -0,0 +1,177 @@
+using System;
+
+namespace Terminal.Gui;
+
+// TODO: v2 - Missing 3D effect - 3D effects will be drawn by a mechanism separate from Adornments
+// TODO: v2 - If a Adornment has focus, navigation keys (e.g Command.NextView) should cycle through SubViews of the Adornments
+// QUESTION: How does a user navigate out of an Adornment to another Adornment, or back into the Parent's SubViews?
+
+///
+/// Adornments are a special form of that appear outside of the :
+/// , , and . They are defined using the
+/// class, which specifies the thickness of the sides of a rectangle.
+///
+///
+///
+/// There is no prevision for creating additional subclasses of Adornment. It is not abstract to enable unit testing.
+///
+///
+/// Each of , , and can be customized.
+///
+///
+public class Adornment : View {
+ ///
+ public Adornment () { /* Do nothing; A parameter-less constructor is required to support all views unit tests. */ }
+
+ ///
+ /// Constructs a new adornment for the view specified by .
+ ///
+ ///
+ public Adornment (View parent) => Parent = parent;
+
+ Thickness _thickness = Thickness.Empty;
+
+ ///
+ /// The Parent of this Adornment (the View this Adornment surrounds).
+ ///
+ ///
+ /// Adornments are distinguished from typical View classes in that they are not sub-views,
+ /// but have a parent/child relationship with their containing View.
+ ///
+ public View Parent { get; set; }
+
+ ///
+ /// Adornments cannot be used as sub-views (see ); this method always throws an .
+ /// TODO: Are we sure?
+ ///
+ public override View SuperView {
+ get => null;
+ set => throw new NotImplementedException ();
+ }
+
+ ///
+ /// Adornments only render to their 's or Parent's SuperView's LineCanvas,
+ /// so setting this property throws an .
+ ///
+ public override bool SuperViewRendersLineCanvas {
+ get => false; // throw new NotImplementedException ();
+ set => throw new NotImplementedException ();
+ }
+
+ ///
+ /// Defines the rectangle that the will use to draw its content.
+ ///
+ public Thickness Thickness {
+ get => _thickness;
+ set {
+ var prev = _thickness;
+ _thickness = value;
+ if (prev != _thickness) {
+
+ Parent?.LayoutAdornments ();
+ OnThicknessChanged (prev);
+ }
+
+ }
+ }
+
+ ///
+ /// Gets the rectangle that describes the inner area of the Adornment. The Location is always (0,0).
+ ///
+ public override Rect Bounds {
+ get => Thickness?.GetInside (new Rect (Point.Empty, Frame.Size)) ?? new Rect (Point.Empty, Frame.Size);
+ set => throw new InvalidOperationException ("It makes no sense to set Bounds of a Thickness.");
+ }
+
+ internal override Adornment CreateAdornment (Type adornmentType)
+ {
+ /* Do nothing - Adornments do not have Adornments */
+ return null;
+ }
+
+ internal override void LayoutAdornments ()
+ {
+ /* Do nothing - Adornments do not have Adornments */
+ }
+
+ ///
+ public override void BoundsToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true)
+ {
+ // Adornments are *Children* of a View, not SubViews. Thus View.BoundsToScreen will not work.
+ // To get the screen-relative coordinates of a Adornment, we need to know who
+ // the Parent is
+ var parentFrame = Parent?.Frame ?? Frame;
+ rrow = row + parentFrame.Y;
+ rcol = col + parentFrame.X;
+
+ // We now have rcol/rrow in coordinates relative to our View's SuperView. If our View's SuperView has
+ // a SuperView, keep going...
+ Parent?.SuperView?.BoundsToScreen (rcol, rrow, out rcol, out rrow, clipped);
+ }
+
+ ///
+ public override Rect FrameToScreen ()
+ {
+ // Adornments are *Children* of a View, not SubViews. Thus View.FrameToScreen will not work.
+ // To get the screen-relative coordinates of a Adornment, we need to know who
+ // the Parent is
+ var ret = Parent?.Frame ?? Frame;
+ ret.Size = Frame.Size;
+
+ ret.Location = Parent?.FrameToScreen ().Location ?? ret.Location;
+
+ // We now have coordinates relative to our View. If our View's SuperView has
+ // a SuperView, keep going...
+ return ret;
+ }
+
+ ///
+ /// Does nothing for Adornment
+ ///
+ ///
+ public override bool OnDrawAdornments () => false;
+
+ ///
+ /// Does nothing for Adornment
+ ///
+ ///
+ public override bool OnRenderLineCanvas () => false;
+
+ ///
+ /// Redraws the Adornments that comprise the .
+ ///
+ public override void OnDrawContent (Rect contentArea)
+ {
+ if (Thickness == Thickness.Empty) {
+ return;
+ }
+
+ var screenBounds = BoundsToScreen (Frame);
+
+ Attribute normalAttr = GetNormalColor ();
+
+ // This just draws/clears the thickness, not the insides.
+ Driver.SetAttribute (normalAttr);
+ Thickness.Draw (screenBounds, (string)(Data ?? string.Empty));
+
+ if (!string.IsNullOrEmpty (TextFormatter.Text)) {
+ if (TextFormatter != null) {
+ TextFormatter.Size = Frame.Size;
+ TextFormatter.NeedsFormat = true;
+ }
+ }
+
+ TextFormatter?.Draw (screenBounds, normalAttr, normalAttr, Rect.Empty, false);
+ //base.OnDrawContent (contentArea);
+ }
+
+ ///
+ /// Called whenever the property changes.
+ ///
+ public virtual void OnThicknessChanged (Thickness previousThickness) => ThicknessChanged?.Invoke (this, new ThicknessEventArgs { Thickness = Thickness, PreviousThickness = previousThickness });
+
+ ///
+ /// Fired whenever the property changes.
+ ///
+ public event EventHandler ThicknessChanged;
+}
\ No newline at end of file
diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs
new file mode 100644
index 000000000..84022e65e
--- /dev/null
+++ b/Terminal.Gui/View/Adornment/Border.cs
@@ -0,0 +1,345 @@
+using System;
+using System.Linq;
+
+namespace Terminal.Gui;
+
+///
+/// The Border for a .
+///
+///
+///
+/// Renders a border around the view with the . A border using
+/// will be drawn on the sides of that are greater than zero.
+///
+///
+/// The of will be drawn based on the value of :
+///
+///
+/// If 1:
+///
+/// ┌┤1234├──┐
+/// │ │
+/// └────────┘
+///
+///
+///
+/// If 2:
+///
+/// ┌────┐
+/// ┌┤1234├──┐
+/// │ │
+/// └────────┘
+///
+///
+///
+/// If 3:
+///
+/// ┌────┐
+/// ┌┤1234├──┐
+/// │└────┘ │
+/// │ │
+/// └────────┘
+///
+///
+///
+///
+/// See the class.
+///
+///
+public class Border : Adornment {
+ LineStyle? _lineStyle = null;
+
+ ///
+ public Border () { /* Do nothing; A parameter-less constructor is required to support all views unit tests. */ }
+
+ ///
+ public Border (View parent) : base (parent) { /* Do nothing; View.CreateAdornment requires a constructor that takes a parent */ }
+
+ ///
+ /// The color scheme for the Border. If set to , gets the scheme.
+ /// color scheme.
+ ///
+ public override ColorScheme ColorScheme {
+ get {
+ if (base.ColorScheme != null) {
+ return base.ColorScheme;
+ }
+ return Parent?.ColorScheme;
+ }
+ set {
+ base.ColorScheme = value;
+ Parent?.SetNeedsDisplay ();
+ }
+ }
+
+ ///
+ /// 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 set to no border will be drawn.
+ ///
+ public new LineStyle LineStyle {
+ get {
+ if (_lineStyle.HasValue) {
+ return _lineStyle.Value;
+ }
+ // TODO: Make Border.LineStyle inherit from the SuperView hierarchy
+ // TODO: Right now, Window and FrameView use CM to set BorderStyle, which negates
+ // TODO: all this.
+ return Parent.SuperView?.BorderStyle ?? LineStyle.None;
+ }
+ set => _lineStyle = value;
+ }
+
+ ///
+ public override void OnDrawContent (Rect contentArea)
+ {
+ base.OnDrawContent (contentArea);
+
+ if (Thickness == Thickness.Empty) {
+ return;
+ }
+
+ //Driver.SetAttribute (Colors.Error.Normal);
+ var screenBounds = BoundsToScreen (Frame);
+
+ //OnDrawSubviews (bounds);
+
+ // TODO: v2 - this will eventually be two controls: "BorderView" and "Label" (for the title)
+
+ // The border adornment (and title) are drawn at the outermost edge of border;
+ // For Border
+ // ...thickness extends outward (border/title is always as far in as possible)
+ var borderBounds = new Rect (
+ screenBounds.X + Math.Max (0, Thickness.Left - 1),
+ screenBounds.Y + Math.Max (0, Thickness.Top - 1),
+ Math.Max (0, screenBounds.Width - Math.Max (0, Math.Max (0, Thickness.Left - 1) + Math.Max (0, Thickness.Right - 1))),
+ Math.Max (0, screenBounds.Height - Math.Max (0, Math.Max (0, Thickness.Top - 1) + Math.Max (0, Thickness.Bottom - 1))));
+
+ var topTitleLineY = borderBounds.Y;
+ var titleY = borderBounds.Y;
+ var titleBarsLength = 0; // the little vertical thingies
+ var maxTitleWidth = Math.Min (Parent.Title.GetColumns (), Math.Min (screenBounds.Width - 4, borderBounds.Width - 4));
+ var sideLineLength = borderBounds.Height;
+ var canDrawBorder = borderBounds.Width > 0 && borderBounds.Height > 0;
+
+ if (!string.IsNullOrEmpty (Parent?.Title)) {
+ if (Thickness.Top == 2) {
+ topTitleLineY = borderBounds.Y - 1;
+ titleY = topTitleLineY + 1;
+ titleBarsLength = 2;
+ }
+
+ // ┌────┐
+ //┌┘View└
+ //│
+ if (Thickness.Top == 3) {
+ topTitleLineY = borderBounds.Y - (Thickness.Top - 1);
+ titleY = topTitleLineY + 1;
+ titleBarsLength = 3;
+ sideLineLength++;
+ }
+
+ // ┌────┐
+ //┌┘View└
+ //│
+ if (Thickness.Top > 3) {
+ topTitleLineY = borderBounds.Y - 2;
+ titleY = topTitleLineY + 1;
+ titleBarsLength = 3;
+ sideLineLength++;
+ }
+
+ }
+
+ if (canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title)) {
+ var prevAttr = Driver.GetAttribute ();
+ Driver.SetAttribute (Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor ());
+ DrawTitle (new Rect (borderBounds.X, titleY, maxTitleWidth, 1), Parent?.Title);
+ Driver.SetAttribute (prevAttr);
+ }
+
+ if (canDrawBorder && LineStyle != LineStyle.None) {
+ var lc = Parent?.LineCanvas;
+
+ var drawTop = Thickness.Top > 0 && Frame.Width > 1 && Frame.Height > 1;
+ var drawLeft = Thickness.Left > 0 && (Frame.Height > 1 || Thickness.Top == 0);
+ var drawBottom = Thickness.Bottom > 0 && Frame.Width > 1;
+ var drawRight = Thickness.Right > 0 && (Frame.Height > 1 || Thickness.Top == 0);
+
+ var prevAttr = Driver.GetAttribute ();
+ if (ColorScheme != null) {
+ Driver.SetAttribute (GetNormalColor ());
+ } else {
+ Driver.SetAttribute (Parent.GetNormalColor ());
+ }
+
+ if (drawTop) {
+ // ╔╡Title╞═════╗
+ // ╔╡╞═════╗
+ if (borderBounds.Width < 4 || string.IsNullOrEmpty (Parent?.Title)) {
+ // ╔╡╞╗ should be ╔══╗
+ lc.AddLine (new Point (borderBounds.Location.X, titleY), borderBounds.Width, Orientation.Horizontal, LineStyle, Driver.GetAttribute ());
+ } else {
+
+ // ┌────┐
+ //┌┘View└
+ //│
+ if (Thickness.Top == 2) {
+ lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, LineStyle, Driver.GetAttribute ());
+ }
+ // ┌────┐
+ //┌┘View└
+ //│
+ if (borderBounds.Width >= 4 && Thickness.Top > 2) {
+ lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, LineStyle, Driver.GetAttribute ());
+ lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY + 2), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, LineStyle, Driver.GetAttribute ());
+ }
+
+ // ╔╡Title╞═════╗
+ // Add a short horiz line for ╔╡
+ lc.AddLine (new Point (borderBounds.Location.X, titleY), 2, Orientation.Horizontal, LineStyle, Driver.GetAttribute ());
+ // Add a vert line for ╔╡
+ lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), titleBarsLength, Orientation.Vertical, LineStyle.Single, Driver.GetAttribute ());
+ // Add a vert line for ╞
+ lc.AddLine (new Point (borderBounds.X + 1 + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2) - 1, topTitleLineY), titleBarsLength, Orientation.Vertical, LineStyle.Single, Driver.GetAttribute ());
+ // Add the right hand line for ╞═════╗
+ lc.AddLine (new Point (borderBounds.X + 1 + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2) - 1, titleY), borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, LineStyle, Driver.GetAttribute ());
+ }
+ }
+ if (drawLeft) {
+ lc.AddLine (new Point (borderBounds.Location.X, titleY), sideLineLength, Orientation.Vertical, LineStyle, Driver.GetAttribute ());
+ }
+ if (drawBottom) {
+ lc.AddLine (new Point (borderBounds.X, borderBounds.Y + borderBounds.Height - 1), borderBounds.Width, Orientation.Horizontal, LineStyle, Driver.GetAttribute ());
+ }
+ if (drawRight) {
+ lc.AddLine (new Point (borderBounds.X + borderBounds.Width - 1, titleY), sideLineLength, Orientation.Vertical, LineStyle, Driver.GetAttribute ());
+ }
+ Driver.SetAttribute (prevAttr);
+
+ // TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler
+ if ((ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler) {
+ // Top
+ var hruler = new Ruler { Length = screenBounds.Width, Orientation = Orientation.Horizontal };
+ if (drawTop) {
+ hruler.Draw (new Point (screenBounds.X, screenBounds.Y));
+ }
+
+ // Redraw title
+ if (drawTop && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title)) {
+ prevAttr = Driver.GetAttribute ();
+ if (ColorScheme != null) {
+ Driver.SetAttribute (HasFocus ? GetHotNormalColor () : GetNormalColor ());
+ } else {
+ Driver.SetAttribute (Parent.HasFocus ? Parent.GetHotNormalColor () : Parent.GetNormalColor ());
+ }
+ DrawTitle (new Rect (borderBounds.X, titleY, Parent.Title.GetColumns (), 1), Parent?.Title);
+ Driver.SetAttribute (prevAttr);
+ }
+
+ //Left
+ var vruler = new Ruler { Length = screenBounds.Height - 2, Orientation = Orientation.Vertical };
+ if (drawLeft) {
+ vruler.Draw (new Point (screenBounds.X, screenBounds.Y + 1), 1);
+ }
+
+ // Bottom
+ if (drawBottom) {
+ hruler.Draw (new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1));
+ }
+
+ // Right
+ if (drawRight) {
+ vruler.Draw (new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1);
+ }
+
+ }
+ }
+
+ //base.OnDrawContent (contentArea);
+ }
+
+ ///
+ /// Draws the title for a Window-style view.
+ ///
+ /// Screen relative region where the title will be drawn.
+ /// The title.
+ public void DrawTitle (Rect region, string title)
+ {
+ var width = region.Width;
+ if (!string.IsNullOrEmpty (title)) {
+ Driver.Move (region.X + 2, region.Y);
+ //Driver.AddRune (' ');
+ var str = title.EnumerateRunes ().Sum (r => Math.Max (r.GetColumns (), 1)) >= width
+ ? TextFormatter.Format (title, width, false, false) [0] : title;
+ Driver.AddStr (str);
+ }
+ }
+
+ ///
+ /// Draws a frame in the current view, clipped by the boundary of this view
+ ///
+ /// View-relative region for the frame to be drawn.
+ /// If set to it clear the region.
+ [Obsolete ("This method is obsolete in v2. Use use LineCanvas or Frame instead.", false)]
+ public void DrawFrame (Rect region, bool clear)
+ {
+ var savedClip = ClipToBounds ();
+ var screenBounds = BoundsToScreen (region);
+
+ if (clear) {
+ Driver.FillRect (region);
+ }
+
+ var lc = new LineCanvas ();
+ var drawTop = region.Width > 1 && region.Height > 1;
+ var drawLeft = region.Width > 1 && region.Height > 1;
+ var drawBottom = region.Width > 1 && region.Height > 1;
+ var drawRight = region.Width > 1 && region.Height > 1;
+
+ if (drawTop) {
+ lc.AddLine (screenBounds.Location, screenBounds.Width, Orientation.Horizontal, LineStyle);
+ }
+ if (drawLeft) {
+ lc.AddLine (screenBounds.Location, screenBounds.Height, Orientation.Vertical, LineStyle);
+ }
+ if (drawBottom) {
+ lc.AddLine (new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1), screenBounds.Width, Orientation.Horizontal, LineStyle);
+ }
+ if (drawRight) {
+ lc.AddLine (new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y), screenBounds.Height, Orientation.Vertical, LineStyle);
+ }
+ foreach (var p in lc.GetMap ()) {
+ Driver.Move (p.Key.X, p.Key.Y);
+ Driver.AddRune (p.Value);
+ }
+ lc.Clear ();
+
+ // TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler
+ if ((ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler) {
+ // Top
+ var hruler = new Ruler { Length = screenBounds.Width, Orientation = Orientation.Horizontal };
+ if (drawTop) {
+ hruler.Draw (new Point (screenBounds.X, screenBounds.Y));
+ }
+
+ //Left
+ var vruler = new Ruler { Length = screenBounds.Height - 2, Orientation = Orientation.Vertical };
+ if (drawLeft) {
+ vruler.Draw (new Point (screenBounds.X, screenBounds.Y + 1), 1);
+ }
+
+ // Bottom
+ if (drawBottom) {
+ hruler.Draw (new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1));
+ }
+
+ // Right
+ if (drawRight) {
+ vruler.Draw (new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1);
+ }
+ }
+
+ Driver.Clip = savedClip;
+ }
+}
diff --git a/Terminal.Gui/View/Adornment/Margin.cs b/Terminal.Gui/View/Adornment/Margin.cs
new file mode 100644
index 000000000..e90ea0e62
--- /dev/null
+++ b/Terminal.Gui/View/Adornment/Margin.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+
+namespace Terminal.Gui;
+
+///
+/// The Margin for a .
+///
+///
+///
+/// See the class.
+///
+///
+public class Margin : Adornment {
+ ///
+ public Margin () { /* Do nothing; A parameter-less constructor is required to support all views unit tests. */ }
+
+ ///
+ public Margin (View parent) : base (parent) { /* Do nothing; View.CreateAdornment requires a constructor that takes a parent */ }
+
+ ///
+ /// The color scheme for the Margin. If set to , gets the 's scheme.
+ /// color scheme.
+ ///
+ public override ColorScheme ColorScheme {
+ get {
+ if (base.ColorScheme != null) {
+ return base.ColorScheme;
+ }
+ return Parent?.SuperView?.ColorScheme ?? Colors.TopLevel;
+ }
+ set {
+ base.ColorScheme = value;
+ Parent?.SetNeedsDisplay ();
+ }
+ }
+}
diff --git a/Terminal.Gui/View/Adornment/Padding.cs b/Terminal.Gui/View/Adornment/Padding.cs
new file mode 100644
index 000000000..2a97074af
--- /dev/null
+++ b/Terminal.Gui/View/Adornment/Padding.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+
+namespace Terminal.Gui;
+
+///
+/// The Padding for a .
+///
+///
+///
+/// See the class.
+///
+///
+public class Padding : Adornment {
+ ///
+ public Padding () { /* Do nothing; A parameter-less constructor is required to support all views unit tests. */ }
+
+ ///
+ public Padding (View parent) : base (parent) { /* Do nothing; View.CreateAdornment requires a constructor that takes a parent */ }
+
+ ///
+ /// The color scheme for the Padding. If set to , gets the scheme.
+ /// color scheme.
+ ///
+ public override ColorScheme ColorScheme {
+ get {
+ if (base.ColorScheme != null) {
+ return base.ColorScheme;
+ }
+ return Parent?.ColorScheme;
+ }
+ set {
+ base.ColorScheme = value;
+ Parent?.SetNeedsDisplay ();
+ }
+ }
+}
diff --git a/Terminal.Gui/View/Frame.cs b/Terminal.Gui/View/Frame.cs
deleted file mode 100644
index 484ce3cd1..000000000
--- a/Terminal.Gui/View/Frame.cs
+++ /dev/null
@@ -1,446 +0,0 @@
-using System.Text;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Xml.Linq;
-using static Terminal.Gui.TileView;
-
-namespace Terminal.Gui {
-
- // TODO: v2 - Missing 3D effect - 3D effects will be drawn by a mechanism separate from Frames
- // TODO: v2 - If a Frame has focus, navigation keys (e.g Command.NextView) should cycle through SubViews of the Frame
- // QUESTION: How does a user navigate out of a Frame to another Frame, or back into the Parent's SubViews?
-
- ///
- /// Frames are a special form of that act as adornments; they appear outside of the
- /// enabling borders, menus, etc...
- ///
- public class Frame : View {
- private Thickness _thickness = Thickness.Empty;
-
- internal override void CreateFrames () { /* Do nothing - Frames do not have Frames */ }
- internal override void LayoutFrames () { /* Do nothing - Frames do not have Frames */ }
-
- ///
- /// The Parent of this Frame (the View this Frame surrounds).
- ///
- public View Parent { get; set; }
-
- ///
- /// Frames cannot be used as sub-views, so this method always throws an .
- /// TODO: Are we sure?
- ///
- public override View SuperView {
- get {
- return null;
- }
- set {
- throw new NotImplementedException ();
- }
- }
-
- ///
- public override void BoundsToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true)
- {
- // Frames are *Children* of a View, not SubViews. Thus View.BoundsToScreen will not work.
- // To get the screen-relative coordinates of a Frame, we need to know who
- // the Parent is
- var parentFrame = Parent?.Frame ?? Frame;
- rrow = row + parentFrame.Y;
- rcol = col + parentFrame.X;
-
- // We now have rcol/rrow in coordinates relative to our View's SuperView. If our View's SuperView has
- // a SuperView, keep going...
- Parent?.SuperView?.BoundsToScreen (rcol, rrow, out rcol, out rrow, clipped);
- }
-
- ///
- public override Rect FrameToScreen ()
- {
- // Frames are *Children* of a View, not SubViews. Thus View.FrameToScreen will not work.
- // To get the screen-relative coordinates of a Frame, we need to know who
- // the Parent is
- var ret = Parent?.Frame ?? Frame;
- ret.Size = Frame.Size;
-
- ret.Location = Parent?.FrameToScreen ().Location ?? ret.Location;
-
- // We now have coordinates relative to our View. If our View's SuperView has
- // a SuperView, keep going...
- return ret;
- }
-
- ///
- /// Does nothing for Frame
- ///
- ///
- public override bool OnDrawFrames () => false;
-
- ///
- /// Does nothing for Frame
- ///
- ///
- public override bool OnRenderLineCanvas () => false;
-
- ///
- /// Frames only render to their Parent or Parent's SuperView's LineCanvas,
- /// so this always throws an .
- ///
- public override bool SuperViewRendersLineCanvas {
- get {
- return false;// throw new NotImplementedException ();
- }
- set {
- throw new NotImplementedException ();
- }
- }
-
- ///
- ///
- ///
- ///
- public virtual void OnDrawSubViews (Rect clipRect)
- {
- // TODO: Enable subviews of Frames (adornments).
- // if (Subviews == null) {
- // return;
- // }
-
- // foreach (var view in Subviews) {
- // // BUGBUG: v2 - shouldn't this be !view.LayoutNeeded? Why draw if layout is going to happen and we'll just draw again?
- // if (view.LayoutNeeded) {
- // view.LayoutSubviews ();
- // }
- // if ((view.Visible && !view.NeedDisplay.IsEmpty && view.Frame.Width > 0 && view.Frame.Height > 0) || view.ChildNeedsDisplay) {
- // view.Redraw (view.Bounds);
-
- // view.NeedDisplay = Rect.Empty;
- // // BUGBUG - v2 why does this need to be set to false?
- // // Shouldn't it be set when the subviews draw?
- // view.ChildNeedsDisplay = false;
- // }
- // }
-
- }
-
- ///
- /// Redraws the Frames that comprise the .
- ///
- public override void OnDrawContent (Rect contentArea)
- {
- if (Thickness == Thickness.Empty) {
- return;
- }
-
- if (ColorScheme != null) {
- Driver.SetAttribute (GetNormalColor ());
- } else {
- if (Id == "Padding") {
- Driver.SetAttribute (new Attribute (Parent.ColorScheme.HotNormal.Background, Parent.ColorScheme.HotNormal.Foreground));
- } else {
- Driver.SetAttribute (Parent.GetNormalColor ());
- }
- }
-
- //Driver.SetAttribute (Colors.Error.Normal);
- var screenBounds = BoundsToScreen (Frame);
-
- // This just draws/clears the thickness, not the insides.
- Thickness.Draw (screenBounds, (string)(Data != null ? Data : string.Empty));
-
- //OnDrawSubviews (bounds);
-
- // TODO: v2 - this will eventually be two controls: "BorderView" and "Label" (for the title)
-
- // The border frame (and title) are drawn at the outermost edge of border;
- // For Border
- // ...thickness extends outward (border/title is always as far in as possible)
- var borderBounds = new Rect (
- screenBounds.X + Math.Max (0, Thickness.Left - 1),
- screenBounds.Y + Math.Max (0, Thickness.Top - 1),
- Math.Max (0, screenBounds.Width - Math.Max (0, Math.Max (0, Thickness.Left - 1) + Math.Max (0, Thickness.Right - 1))),
- Math.Max (0, screenBounds.Height - Math.Max (0, Math.Max (0, Thickness.Top - 1) + Math.Max (0, Thickness.Bottom - 1))));
-
- var topTitleLineY = borderBounds.Y;
- var titleY = borderBounds.Y;
- var titleBarsLength = 0; // the little vertical thingies
- var maxTitleWidth = Math.Min (Parent.Title.GetColumns (), Math.Min (screenBounds.Width - 4, borderBounds.Width - 4));
- var sideLineLength = borderBounds.Height;
- var canDrawBorder = borderBounds.Width > 0 && borderBounds.Height > 0;
-
- if (!string.IsNullOrEmpty (Parent?.Title)) {
- if (Thickness.Top == 2) {
- topTitleLineY = borderBounds.Y - 1;
- titleY = topTitleLineY + 1;
- titleBarsLength = 2;
- }
-
- // ┌────┐
- //┌┘View└
- //│
- if (Thickness.Top == 3) {
- topTitleLineY = borderBounds.Y - (Thickness.Top - 1);
- titleY = topTitleLineY + 1;
- titleBarsLength = 3;
- sideLineLength++;
- }
-
- // ┌────┐
- //┌┘View└
- //│
- if (Thickness.Top > 3) {
- topTitleLineY = borderBounds.Y - 2;
- titleY = topTitleLineY + 1;
- titleBarsLength = 3;
- sideLineLength++;
- }
-
- }
-
- if (Id == "Border" && canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title)) {
- var prevAttr = Driver.GetAttribute ();
- if (ColorScheme != null) {
- Driver.SetAttribute (HasFocus ? GetHotNormalColor () : GetNormalColor ());
- } else {
- Driver.SetAttribute (Parent.HasFocus ? Parent.GetHotNormalColor () : Parent.GetNormalColor ());
- }
- DrawTitle (new Rect (borderBounds.X, titleY, maxTitleWidth, 1), Parent?.Title);
- Driver.SetAttribute (prevAttr);
- }
-
- if (Id == "Border" && canDrawBorder && BorderStyle != LineStyle.None) {
- LineCanvas lc = Parent?.LineCanvas;
-
- var drawTop = Thickness.Top > 0 && Frame.Width > 1 && Frame.Height > 1;
- var drawLeft = Thickness.Left > 0 && (Frame.Height > 1 || Thickness.Top == 0);
- var drawBottom = Thickness.Bottom > 0 && Frame.Width > 1;
- var drawRight = Thickness.Right > 0 && (Frame.Height > 1 || Thickness.Top == 0);
-
- var prevAttr = Driver.GetAttribute ();
- if (ColorScheme != null) {
- Driver.SetAttribute (GetNormalColor ());
- } else {
- Driver.SetAttribute (Parent.GetNormalColor ());
- }
-
- if (drawTop) {
- // ╔╡Title╞═════╗
- // ╔╡╞═════╗
- if (borderBounds.Width < 4 || string.IsNullOrEmpty (Parent?.Title)) {
- // ╔╡╞╗ should be ╔══╗
- lc.AddLine (new Point (borderBounds.Location.X, titleY), borderBounds.Width, Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
- } else {
-
- // ┌────┐
- //┌┘View└
- //│
- if (Thickness.Top == 2) {
- lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
- }
- // ┌────┐
- //┌┘View└
- //│
- if (borderBounds.Width >= 4 && Thickness.Top > 2) {
- lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
- lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY + 2), Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
- }
-
- // ╔╡Title╞═════╗
- // Add a short horiz line for ╔╡
- lc.AddLine (new Point (borderBounds.Location.X, titleY), 2, Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
- // Add a vert line for ╔╡
- lc.AddLine (new Point (borderBounds.X + 1, topTitleLineY), titleBarsLength, Orientation.Vertical, LineStyle.Single, Driver.GetAttribute ());
- // Add a vert line for ╞
- lc.AddLine (new Point (borderBounds.X + 1 + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2) - 1, topTitleLineY), titleBarsLength, Orientation.Vertical, LineStyle.Single, Driver.GetAttribute ());
- // Add the right hand line for ╞═════╗
- lc.AddLine (new Point (borderBounds.X + 1 + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2) - 1, titleY), borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2), Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
- }
- }
- if (drawLeft) {
- lc.AddLine (new Point (borderBounds.Location.X, titleY), sideLineLength, Orientation.Vertical, BorderStyle, Driver.GetAttribute ());
- }
- if (drawBottom) {
- lc.AddLine (new Point (borderBounds.X, borderBounds.Y + borderBounds.Height - 1), borderBounds.Width, Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
- }
- if (drawRight) {
- lc.AddLine (new Point (borderBounds.X + borderBounds.Width - 1, titleY), sideLineLength, Orientation.Vertical, BorderStyle, Driver.GetAttribute ());
- }
- Driver.SetAttribute (prevAttr);
-
- // TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler
- if ((ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler) {
- // Top
- var hruler = new Ruler () { Length = screenBounds.Width, Orientation = Orientation.Horizontal };
- if (drawTop) {
- hruler.Draw (new Point (screenBounds.X, screenBounds.Y));
- }
-
- // Redraw title
- if (drawTop && Id == "Border" && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title)) {
- prevAttr = Driver.GetAttribute ();
- if (ColorScheme != null) {
- Driver.SetAttribute (HasFocus ? GetHotNormalColor () : GetNormalColor ());
- } else {
- Driver.SetAttribute (Parent.HasFocus ? Parent.GetHotNormalColor () : Parent.GetNormalColor ());
- }
- DrawTitle (new Rect (borderBounds.X, titleY, Parent.Title.GetColumns (), 1), Parent?.Title);
- Driver.SetAttribute (prevAttr);
- }
-
- //Left
- var vruler = new Ruler () { Length = screenBounds.Height - 2, Orientation = Orientation.Vertical };
- if (drawLeft) {
- vruler.Draw (new Point (screenBounds.X, screenBounds.Y + 1), 1);
- }
-
- // Bottom
- if (drawBottom) {
- hruler.Draw (new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1));
- }
-
- // Right
- if (drawRight) {
- vruler.Draw (new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1);
- }
-
- }
- }
-
- ClearNeedsDisplay ();
- }
-
- // TODO: v2 - Frame.BorderStyle is temporary - Eventually the border will be drawn by a "BorderView" that is a subview of the Frame.
- ///
- ///
- ///
- public new LineStyle BorderStyle { get; set; } = LineStyle.None;
-
- ///
- /// Defines the rectangle that the will use to draw its content.
- ///
- public Thickness Thickness {
- get { return _thickness; }
- set {
- var prev = _thickness;
- _thickness = value;
- if (prev != _thickness) {
-
- Parent?.LayoutFrames ();
- OnThicknessChanged (prev);
- }
-
- }
- }
-
- ///
- /// Called whenever the property changes.
- ///
- public virtual void OnThicknessChanged (Thickness previousThickness)
- {
- ThicknessChanged?.Invoke (this, new ThicknessEventArgs () { Thickness = Thickness, PreviousThickness = previousThickness });
- }
-
- ///
- /// Fired whenever the property changes.
- ///
- public event EventHandler ThicknessChanged;
-
- ///
- /// Gets the rectangle that describes the inner area of the frame. The Location is always (0,0).
- ///
- public override Rect Bounds {
- get {
- return Thickness?.GetInside (new Rect (Point.Empty, Frame.Size)) ?? new Rect (Point.Empty, Frame.Size);
- }
- set {
- throw new InvalidOperationException ("It makes no sense to set Bounds of a Thickness.");
- }
- }
-
- ///
- /// Draws the title for a Window-style view.
- ///
- /// Screen relative region where the title will be drawn.
- /// The title.
- public void DrawTitle (Rect region, string title)
- {
- var width = region.Width;
- if (!string.IsNullOrEmpty (title)) {
- Driver.Move (region.X + 2, region.Y);
- //Driver.AddRune (' ');
- var str = title.EnumerateRunes ().Sum (r => Math.Max (r.GetColumns (), 1)) >= width
- ? TextFormatter.Format (title, width, false, false) [0] : title;
- Driver.AddStr (str);
- }
- }
-
- ///
- /// Draws a frame in the current view, clipped by the boundary of this view
- ///
- /// View-relative region for the frame to be drawn.
- /// If set to it clear the region.
- [ObsoleteAttribute ("This method is obsolete in v2. Use use LineCanvas or Frame instead.", false)]
- public void DrawFrame (Rect region, bool clear)
- {
- var savedClip = ClipToBounds ();
- var screenBounds = BoundsToScreen (region);
-
- if (clear) {
- Driver.FillRect (region);
- }
-
- var lc = new LineCanvas ();
- var drawTop = region.Width > 1 && region.Height > 1;
- var drawLeft = region.Width > 1 && region.Height > 1;
- var drawBottom = region.Width > 1 && region.Height > 1;
- var drawRight = region.Width > 1 && region.Height > 1;
-
- if (drawTop) {
- lc.AddLine (screenBounds.Location, screenBounds.Width, Orientation.Horizontal, BorderStyle);
- }
- if (drawLeft) {
- lc.AddLine (screenBounds.Location, screenBounds.Height, Orientation.Vertical, BorderStyle);
- }
- if (drawBottom) {
- lc.AddLine (new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1), screenBounds.Width, Orientation.Horizontal, BorderStyle);
- }
- if (drawRight) {
- lc.AddLine (new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y), screenBounds.Height, Orientation.Vertical, BorderStyle);
- }
- foreach (var p in lc.GetMap ()) {
- Driver.Move (p.Key.X, p.Key.Y);
- Driver.AddRune (p.Value);
- }
- lc.Clear ();
-
- // TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler
- if ((ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler) {
- // Top
- var hruler = new Ruler () { Length = screenBounds.Width, Orientation = Orientation.Horizontal };
- if (drawTop) {
- hruler.Draw (new Point (screenBounds.X, screenBounds.Y));
- }
-
- //Left
- var vruler = new Ruler () { Length = screenBounds.Height - 2, Orientation = Orientation.Vertical };
- if (drawLeft) {
- vruler.Draw (new Point (screenBounds.X, screenBounds.Y + 1), 1);
- }
-
- // Bottom
- if (drawBottom) {
- hruler.Draw (new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1));
- }
-
- // Right
- if (drawRight) {
- vruler.Draw (new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1);
- }
- }
-
- Driver.Clip = savedClip;
- }
-
- }
-}
diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs
index 7bb052ba5..b9c93eeb3 100644
--- a/Terminal.Gui/View/Layout/ViewLayout.cs
+++ b/Terminal.Gui/View/Layout/ViewLayout.cs
@@ -86,7 +86,7 @@ public partial class View {
// TODO: Figure out if the below can be optimized.
if (IsInitialized /*|| LayoutStyle == LayoutStyle.Absolute*/) {
- LayoutFrames ();
+ LayoutAdornments ();
SetTextFormatterSize ();
SetNeedsLayout ();
SetNeedsDisplay ();
@@ -100,21 +100,19 @@ public partial class View {
///
///
///
- /// The frames (, , and ) are not part of the View's
- /// content
- /// and are not clipped by the View's Clip Area.
+ /// The adornments (, , and ) are not part of the View's
+ /// content and are not clipped by the View's Clip Area.
///
///
- /// Changing the size of a frame (, , or )
- /// will change the size of the and trigger to update the layout
- /// of the
- /// and its .
+ /// Changing the size of an adornment (, , or )
+ /// will change the size of and trigger to update the layout
+ /// of the and its .
///
///
- public Frame Margin { get; private set; }
+ public Margin Margin { get; private set; }
///
- /// The frame (specified as a ) inside of the view that offsets the from the
+ /// The adornment (specified as a ) inside of the view that offsets the from the
/// .
/// The Border provides the space for a visual border (drawn using line-drawing glyphs) and the Title.
/// The Border expands inward; in other words if `Border.Thickness.Top == 2` the border and
@@ -125,9 +123,8 @@ public partial class View {
/// provides a simple helper for turning a simple border frame on or off.
///
///
- /// The frames (, , and ) are not part of the View's
- /// content
- /// and are not clipped by the View's Clip Area.
+ /// The adornments (, , and ) are not part of the View's
+ /// content and are not clipped by the View's Clip Area.
///
///
/// Changing the size of a frame (, , or )
@@ -136,7 +133,7 @@ public partial class View {
/// and its .
///
///
- public Frame Border { get; private set; }
+ public Border Border { get; private set; }
///
/// Gets or sets whether the view has a one row/col thick border.
@@ -145,12 +142,12 @@ public partial class View {
///
/// This is a helper for manipulating the view's . Setting this property to any value other
/// than
- /// is equivalent to setting 's
+ /// is equivalent to setting 's
/// to `1` and to the value.
///
///
/// Setting this property to is equivalent to setting 's
- ///
+ ///
/// to `0` and to .
///
///
@@ -158,18 +155,15 @@ public partial class View {
///
///
public LineStyle BorderStyle {
- get => Border?.BorderStyle ?? LineStyle.None;
+ get => Border.LineStyle;
set {
- if (Border == null) {
- throw new InvalidOperationException ("Border is null; this is likely a bug.");
- }
if (value != LineStyle.None) {
Border.Thickness = new Thickness (1);
} else {
Border.Thickness = new Thickness (0);
}
- Border.BorderStyle = value;
- LayoutFrames ();
+ Border.LineStyle = value;
+ LayoutAdornments ();
SetNeedsLayout ();
}
}
@@ -180,9 +174,8 @@ public partial class View {
///
///
///
- /// The frames (, , and ) are not part of the View's
- /// content
- /// and are not clipped by the View's Clip Area.
+ /// The adornments (, , and ) are not part of the View's
+ /// content and are not clipped by the View's Clip Area.
///
///
/// Changing the size of a frame (, , or )
@@ -191,24 +184,73 @@ public partial class View {
/// and its .
///
///
- public Frame Padding { get; private set; }
+ public Padding Padding { get; private set; }
///
///
- /// Gets the LayoutStyle for the .
- ///
- ///
- /// If Absolute, the , , , and
- ///
- /// objects are all absolute values and are not relative. The position and size of the view is described by
- /// .
- ///
- ///
- /// If Computed, one or more of the , , , or
- ///
- /// objects are relative to the and are computed at layout time.
+ /// Gets the thickness describing the sum of the Adornments' thicknesses.
///
///
+ /// A thickness that describes the sum of the Adornments' thicknesses.
+ public Thickness GetAdornmentsThickness ()
+ {
+ int left = Margin.Thickness.Left + Border.Thickness.Left + Padding.Thickness.Left;
+ int top = Margin.Thickness.Top + Border.Thickness.Top + Padding.Thickness.Top;
+ int right = Margin.Thickness.Right + Border.Thickness.Right + Padding.Thickness.Right;
+ int bottom = Margin.Thickness.Bottom + Border.Thickness.Bottom + Padding.Thickness.Bottom;
+ return new Thickness (left, top, right, bottom);
+ }
+
+ ///
+ /// Helper to get the X and Y offset of the Bounds from the Frame. This is the sum of the Left and Top properties of
+ /// , and .
+ ///
+ public Point GetBoundsOffset () => new (Padding?.Thickness.GetInside (Padding.Frame).X ?? 0, Padding?.Thickness.GetInside (Padding.Frame).Y ?? 0);
+
+ ///
+ /// This internal method is overridden by Adornment to do nothing to prevent recursion during View construction.
+ /// And, because Adornments don't have Adornments. It's internal to support unit tests.
+ ///
+ ///
+ ///
+ ///
+ internal virtual Adornment CreateAdornment (Type adornmentType)
+ {
+ void ThicknessChangedHandler (object sender, EventArgs e)
+ {
+ if (IsInitialized) {
+ LayoutAdornments ();
+ }
+ SetNeedsLayout ();
+ SetNeedsDisplay ();
+ }
+
+ Adornment adornment;
+
+ adornment = Activator.CreateInstance (adornmentType, this) as Adornment;
+ adornment.ThicknessChanged += ThicknessChangedHandler;
+
+ return adornment;
+ }
+
+ ///
+ /// Controls how the View's is computed during . If the style is set to
+ /// , LayoutSubviews does not change the .
+ /// If the style is the is updated using
+ /// the , , , and properties.
+ ///
+ ///
+ ///
+ /// Setting this property to will cause to determine the
+ /// size and position of the view. and will be set to using .
+ ///
+ ///
+ /// Setting this property to will cause the view to use the method to
+ /// size and position of the view. If either of the and properties are `null` they will be set to using
+ /// the current value of .
+ /// If either of the and properties are `null` they will be set to using .
+ ///
+ ///
/// The layout style.
public LayoutStyle LayoutStyle {
get {
@@ -252,8 +294,13 @@ public partial class View {
Debug.WriteLine ($"WARNING: Bounds is being accessed before the View has been initialized. This is likely a bug in {this}");
}
#endif // DEBUG
- var frameRelativeBounds = FrameGetInsideBounds ();
- return new Rect (default, frameRelativeBounds.Size);
+ // BUGBUG: I think there's a bug here. This should be && not ||
+ if (Margin == null || Border == null || Padding == null) {
+ return new Rect (default, Frame.Size);
+ }
+ var width = Math.Max (0, Frame.Size.Width - Margin.Thickness.Horizontal - Border.Thickness.Horizontal - Padding.Thickness.Horizontal);
+ var height = Math.Max (0, Frame.Size.Height - Margin.Thickness.Vertical - Border.Thickness.Vertical - Padding.Thickness.Vertical);
+ return new Rect (Point.Empty, new Size (width, height));
}
set {
// TODO: Should we enforce Bounds.X/Y == 0? The code currently ignores value.X/Y which is
@@ -470,76 +517,7 @@ public partial class View {
///
public event EventHandler Initialized;
- ///
- /// Helper to get the total thickness of the , , and .
- ///
- /// A thickness that describes the sum of the Frames' thicknesses.
- public Thickness GetFramesThickness ()
- {
- var left = Margin.Thickness.Left + Border.Thickness.Left + Padding.Thickness.Left;
- var top = Margin.Thickness.Top + Border.Thickness.Top + Padding.Thickness.Top;
- var right = Margin.Thickness.Right + Border.Thickness.Right + Padding.Thickness.Right;
- var bottom = Margin.Thickness.Bottom + Border.Thickness.Bottom + Padding.Thickness.Bottom;
- return new Thickness (left, top, right, bottom);
- }
- ///
- /// Helper to get the X and Y offset of the Bounds from the Frame. This is the sum of the Left and Top properties of
- /// , and .
- ///
- public Point GetBoundsOffset () => new (Padding?.Thickness.GetInside (Padding.Frame).X ?? 0, Padding?.Thickness.GetInside (Padding.Frame).Y ?? 0);
-
- ///
- /// Creates the view's objects. This internal method is overridden by Frame to do nothing
- /// to prevent recursion during View construction.
- ///
- internal virtual void CreateFrames ()
- {
- void ThicknessChangedHandler (object sender, EventArgs e)
- {
- if (IsInitialized) {
- LayoutFrames ();
- }
- SetNeedsLayout ();
- SetNeedsDisplay ();
- }
-
- if (Margin != null) {
- Margin.ThicknessChanged -= ThicknessChangedHandler;
- Margin.Dispose ();
- }
- Margin = new Frame { Id = "Margin", Thickness = new Thickness (0) };
- Margin.ThicknessChanged += ThicknessChangedHandler;
- Margin.Parent = this;
-
- if (Border != null) {
- Border.ThicknessChanged -= ThicknessChangedHandler;
- Border.Dispose ();
- }
- Border = new Frame { Id = "Border", Thickness = new Thickness (0) };
- Border.ThicknessChanged += ThicknessChangedHandler;
- Border.Parent = this;
-
- // TODO: Create View.AddAdornment
-
- if (Padding != null) {
- Padding.ThicknessChanged -= ThicknessChangedHandler;
- Padding.Dispose ();
- }
- Padding = new Frame { Id = "Padding", Thickness = new Thickness (0) };
- Padding.ThicknessChanged += ThicknessChangedHandler;
- Padding.Parent = this;
- }
-
- Rect FrameGetInsideBounds ()
- {
- if (Margin == null || Border == null || Padding == null) {
- return new Rect (default, Frame.Size);
- }
- var width = Math.Max (0, Frame.Size.Width - Margin.Thickness.Horizontal - Border.Thickness.Horizontal - Padding.Thickness.Horizontal);
- var height = Math.Max (0, Frame.Size.Height - Margin.Thickness.Vertical - Border.Thickness.Vertical - Padding.Thickness.Vertical);
- return new Rect (Point.Empty, new Size (width, height));
- }
// Diagnostics to highlight when X or Y is read before the view has been initialized
Pos VerifyIsInitialized (Pos pos, string member)
@@ -588,7 +566,7 @@ public partial class View {
// TODO: Determine what, if any of the below is actually needed here.
if (IsInitialized) {
SetFrameToFitText ();
- LayoutFrames ();
+ LayoutAdornments ();
SetTextFormatterSize ();
SetNeedsLayout ();
SetNeedsDisplay ();
@@ -856,10 +834,8 @@ public partial class View {
if (IsInitialized) {
// TODO: Figure out what really is needed here. All unit tests (except AutoSize) pass as-is
- //LayoutFrames ();
SetTextFormatterSize ();
SetNeedsLayout ();
- //SetNeedsDisplay ();
}
// BUGBUG: Why is this AFTER setting Frame? Seems duplicative.
@@ -1019,12 +995,12 @@ public partial class View {
} // TopologicalSort
///
- /// Overriden by to do nothing, as the does not have frames.
+ /// Overriden by to do nothing, as the does not have adornments.
///
- internal virtual void LayoutFrames ()
+ internal virtual void LayoutAdornments ()
{
if (Margin == null) {
- return; // CreateFrames() has not been called yet
+ return; // CreateAdornments () has not been called yet
}
if (Margin.Frame.Size != Frame.Size) {
@@ -1083,7 +1059,7 @@ public partial class View {
return;
}
- LayoutFrames ();
+ LayoutAdornments ();
var oldBounds = Bounds;
OnLayoutStarted (new LayoutEventArgs { OldBounds = oldBounds });
diff --git a/Terminal.Gui/View/View.cs b/Terminal.Gui/View/View.cs
index df3abf059..54e8d0c73 100644
--- a/Terminal.Gui/View/View.cs
+++ b/Terminal.Gui/View/View.cs
@@ -68,7 +68,7 @@ namespace Terminal.Gui;
/// points. The Width and Height properties are of type
/// and can use absolute position,
/// percentages, and anchors. These are useful as they will take
-/// care of repositioning views when view's frames are resized or
+/// care of repositioning views when view's adornments are resized or
/// if the terminal size changes.
///
///
@@ -88,7 +88,7 @@ namespace Terminal.Gui;
/// The method is invoked when the size or layout of a view has
/// changed. The default processing system will keep the size and dimensions
/// for views that use the , and will recompute the
-/// frames for the vies that use .
+/// Adornments for the views that use .
///
///
/// Views have a property that defines the default colors that subviews
@@ -430,9 +430,9 @@ public partial class View : Responder, ISupportInitializeNotification {
/// control the size and location of the view, changing it to .
///
///
- /// Location.
+ /// Location.
/// text to initialize the property with.
- public View (Rect rect, string text) => SetInitialProperties (text, rect, LayoutStyle.Absolute);
+ public View (Rect frame, string text) => SetInitialProperties (text, frame, LayoutStyle.Absolute);
///
@@ -493,7 +493,9 @@ public partial class View : Responder, ISupportInitializeNotification {
AddCommands ();
- CreateFrames ();
+ Margin = CreateAdornment (typeof (Margin)) as Margin;
+ Border = CreateAdornment (typeof (Border)) as Border;
+ Padding = CreateAdornment (typeof (Padding)) as Padding;
}
///
@@ -534,12 +536,6 @@ public partial class View : Responder, ISupportInitializeNotification {
if (!IsInitialized) {
_oldCanFocus = CanFocus;
_oldTabIndex = _tabIndex;
-
-
- // TODO: Figure out why ScrollView and other tests fail if this call is put here
- // instead of the constructor.
- //InitializeFrames ();
-
}
//throw new InvalidOperationException ("The view is already initialized.");
diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs
index 5c708c041..6a3c29b52 100644
--- a/Terminal.Gui/View/ViewDrawing.cs
+++ b/Terminal.Gui/View/ViewDrawing.cs
@@ -58,7 +58,7 @@ public partial class View {
/// Gets or sets whether this View will use it's SuperView's for
/// rendering any border lines. If the rendering of any borders drawn
/// by this Frame will be done by it's parent's SuperView. If (the default)
- /// this View's method will be called to render the borders.
+ /// this View's method will be called to render the borders.
///
public virtual bool SuperViewRendersLineCanvas { get; set; } = false;
@@ -87,7 +87,14 @@ public partial class View {
/// or if is .
/// If it's overridden can return other values.
///
- public virtual Attribute GetFocusColor () => Enabled ? ColorScheme.Focus : ColorScheme.Disabled;
+ public virtual Attribute GetFocusColor ()
+ {
+ var cs = ColorScheme;
+ if (ColorScheme == null) {
+ cs = new ColorScheme ();
+ }
+ return Enabled ? cs.Focus : cs.Disabled;
+ }
///
/// Determines the current based on the value.
@@ -97,7 +104,14 @@ public partial class View {
/// or if is .
/// If it's overridden can return other values.
///
- public virtual Attribute GetHotNormalColor () => Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled;
+ public virtual Attribute GetHotNormalColor ()
+ {
+ var cs = ColorScheme;
+ if (ColorScheme == null) {
+ cs = new ColorScheme ();
+ }
+ return Enabled ? cs.HotNormal : cs.Disabled;
+ }
///
/// Displays the specified character in the specified column and row of the View.
@@ -334,7 +348,7 @@ public partial class View {
/// method will cause the be prepared to be rendered.
///
///
- public virtual bool OnDrawFrames ()
+ public virtual bool OnDrawAdornments ()
{
if (!IsInitialized) {
return false;
@@ -372,7 +386,7 @@ public partial class View {
if (!CanBeVisible (this)) {
return;
}
- OnDrawFrames ();
+ OnDrawAdornments ();
var prevClip = ClipToBounds ();
@@ -474,7 +488,7 @@ public partial class View {
{
if (NeedsDisplay) {
if (SuperView != null) {
- Clear (BoundsToScreen (Bounds));
+ Clear (BoundsToScreen (contentArea));
}
if (!string.IsNullOrEmpty (TextFormatter.Text)) {
@@ -483,7 +497,7 @@ public partial class View {
}
}
// This should NOT clear
- TextFormatter?.Draw (BoundsToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
+ TextFormatter?.Draw (BoundsToScreen (contentArea), HasFocus ? GetFocusColor () : GetNormalColor (),
HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
Rect.Empty, false);
SetSubViewNeedsDisplay ();
diff --git a/Terminal.Gui/Views/ColorPicker.cs b/Terminal.Gui/Views/ColorPicker.cs
index 3e74e1793..b4a5dc923 100644
--- a/Terminal.Gui/Views/ColorPicker.cs
+++ b/Terminal.Gui/Views/ColorPicker.cs
@@ -112,7 +112,7 @@ public class ColorPicker : View {
AddCommands ();
AddKeyBindings ();
LayoutStarted += (o, a) => {
- var thickness = GetFramesThickness ();
+ var thickness = GetAdornmentsThickness ();
Width = _cols * BoxWidth + thickness.Vertical;
Height = _rows * BoxHeight + thickness.Horizontal;
};
diff --git a/Terminal.Gui/Views/DatePicker.cs b/Terminal.Gui/Views/DatePicker.cs
index 109752a7a..0ee994568 100644
--- a/Terminal.Gui/Views/DatePicker.cs
+++ b/Terminal.Gui/Views/DatePicker.cs
@@ -8,7 +8,7 @@ using System.Data;
using System.Globalization;
using System.Linq;
-namespace Terminal.Gui.Views;
+namespace Terminal.Gui;
///
/// The Date Picker.
diff --git a/Terminal.Gui/Views/FrameView.cs b/Terminal.Gui/Views/FrameView.cs
index 9e9b4f33d..c5b62d74c 100644
--- a/Terminal.Gui/Views/FrameView.cs
+++ b/Terminal.Gui/Views/FrameView.cs
@@ -50,7 +50,7 @@ namespace Terminal.Gui {
{
this.Title = title;
Border.Thickness = new Thickness (1);
- Border.BorderStyle = DefaultBorderStyle;
+ Border.LineStyle = DefaultBorderStyle;
//Border.ColorScheme = ColorScheme;
Border.Data = "Border";
}
diff --git a/Terminal.Gui/Views/Line.cs b/Terminal.Gui/Views/Line.cs
index ac2c3c3ff..35829fb39 100644
--- a/Terminal.Gui/Views/Line.cs
+++ b/Terminal.Gui/Views/Line.cs
@@ -23,7 +23,7 @@ namespace Terminal.Gui {
}
///
- public override bool OnDrawFrames ()
+ public override bool OnDrawAdornments ()
{
var screenBounds = BoundsToScreen (Bounds);
LineCanvas lc;
@@ -43,7 +43,7 @@ namespace Terminal.Gui {
///
public override void OnDrawContent (Rect contentArea)
{
- OnDrawFrames ();
+ OnDrawAdornments ();
}
}
}
diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs
index c5818c75e..1afdef5b7 100644
--- a/Terminal.Gui/Views/Menu/Menu.cs
+++ b/Terminal.Gui/Views/Menu/Menu.cs
@@ -633,7 +633,7 @@ class Menu : View {
Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows);
Driver.SetAttribute (GetNormalColor ());
- OnDrawFrames ();
+ OnDrawAdornments ();
OnRenderLineCanvas ();
for (int i = Bounds.Y; i < _barItems.Children.Length; i++) {
diff --git a/Terminal.Gui/Views/MessageBox.cs b/Terminal.Gui/Views/MessageBox.cs
index 6a4ba3e3b..116b1020f 100644
--- a/Terminal.Gui/Views/MessageBox.cs
+++ b/Terminal.Gui/Views/MessageBox.cs
@@ -303,20 +303,20 @@ namespace Terminal.Gui {
// TODO: replace with Dim.Fit when implemented
var maxBounds = d.SuperView?.Bounds ?? Application.Top.Bounds;
if (wrapMessage) {
- messageLabel.TextFormatter.Size = new Size (maxBounds.Size.Width - d.GetFramesThickness ().Horizontal, maxBounds.Size.Height - d.GetFramesThickness ().Vertical);
+ messageLabel.TextFormatter.Size = new Size (maxBounds.Size.Width - d.GetAdornmentsThickness ().Horizontal, maxBounds.Size.Height - d.GetAdornmentsThickness ().Vertical);
}
var msg = messageLabel.TextFormatter.Format ();
var messageSize = messageLabel.TextFormatter.GetFormattedSize ();
// Ensure the width fits the text + buttons
- var newWidth = Math.Max (width, Math.Max (messageSize.Width + d.GetFramesThickness ().Horizontal,
- d.GetButtonsWidth () + d.buttons.Count + d.GetFramesThickness ().Horizontal));
+ var newWidth = Math.Max (width, Math.Max (messageSize.Width + d.GetAdornmentsThickness ().Horizontal,
+ d.GetButtonsWidth () + d.buttons.Count + d.GetAdornmentsThickness ().Horizontal));
if (newWidth > d.Frame.Width) {
d.Width = newWidth;
}
// Ensure height fits the text + vspace + buttons
var lastLine = messageLabel.TextFormatter.Lines [^1];
- d.Height = Math.Max (height, messageSize.Height + (lastLine.EndsWith ("\r\n") || lastLine.EndsWith ('\n') ? 1 : 2) + d.GetFramesThickness ().Vertical);
+ d.Height = Math.Max (height, messageSize.Height + (lastLine.EndsWith ("\r\n") || lastLine.EndsWith ('\n') ? 1 : 2) + d.GetAdornmentsThickness ().Vertical);
d.SetRelativeLayout (d.SuperView?.Frame ?? Application.Top.Frame);
};
diff --git a/Terminal.Gui/Views/ProgressBar.cs b/Terminal.Gui/Views/ProgressBar.cs
index b6a7e1638..f0249048b 100644
--- a/Terminal.Gui/Views/ProgressBar.cs
+++ b/Terminal.Gui/Views/ProgressBar.cs
@@ -83,7 +83,7 @@ public class ProgressBar : View {
void ProgressBar_LayoutStarted (object sender, EventArgs e)
{
// TODO: use Dim.Auto
- Height = 1 + GetFramesThickness ().Vertical;
+ Height = 1 + GetAdornmentsThickness ().Vertical;
}
float _fraction;
diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs
index 0061f30c3..e56505e70 100644
--- a/Terminal.Gui/Views/RadioGroup.cs
+++ b/Terminal.Gui/Views/RadioGroup.cs
@@ -124,8 +124,8 @@ public class RadioGroup : View {
case Orientation.Vertical:
var r = MakeRect (0, 0, radioLabels);
if (IsInitialized) {
- Width = r.Width + GetFramesThickness ().Horizontal;
- Height = radioLabels.Count + GetFramesThickness ().Vertical;
+ Width = r.Width + GetAdornmentsThickness ().Horizontal;
+ Height = radioLabels.Count + GetAdornmentsThickness ().Vertical;
}
break;
@@ -136,8 +136,8 @@ public class RadioGroup : View {
length += item.length;
}
if (IsInitialized) {
- Width = length + GetFramesThickness ().Vertical;
- Height = 1 + GetFramesThickness ().Horizontal;
+ Width = length + GetAdornmentsThickness ().Vertical;
+ Height = 1 + GetAdornmentsThickness ().Horizontal;
}
break;
}
diff --git a/Terminal.Gui/Views/Slider.cs b/Terminal.Gui/Views/Slider.cs
index c59d282aa..2c3785aab 100644
--- a/Terminal.Gui/Views/Slider.cs
+++ b/Terminal.Gui/Views/Slider.cs
@@ -808,12 +808,12 @@ public class Slider : View {
Height = 0;
if (_config._sliderOrientation == Orientation.Horizontal) {
Bounds = new Rect (Bounds.Location,
- new Size (int.Min (SuperView.Bounds.Width - GetFramesThickness ().Horizontal, CalcBestLength ()),
- int.Min (SuperView.Bounds.Height - GetFramesThickness ().Vertical, CalcThickness ())));
+ new Size (int.Min (SuperView.Bounds.Width - GetAdornmentsThickness ().Horizontal, CalcBestLength ()),
+ int.Min (SuperView.Bounds.Height - GetAdornmentsThickness ().Vertical, CalcThickness ())));
} else {
Bounds = new Rect (Bounds.Location,
- new Size (int.Min (SuperView.Bounds.Width - GetFramesThickness ().Horizontal, CalcThickness ()),
- int.Min (SuperView.Bounds.Height - GetFramesThickness ().Vertical, CalcBestLength ())));
+ new Size (int.Min (SuperView.Bounds.Width - GetAdornmentsThickness ().Horizontal, CalcThickness ()),
+ int.Min (SuperView.Bounds.Height - GetAdornmentsThickness ().Vertical, CalcBestLength ())));
}
}
diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs
index e32eef6c8..d19cb7c95 100644
--- a/Terminal.Gui/Views/TileView.cs
+++ b/Terminal.Gui/Views/TileView.cs
@@ -301,7 +301,7 @@ namespace Terminal.Gui {
/// Overridden so no Frames get drawn (BUGBUG: v2 fix this hack)
///
///
- public override bool OnDrawFrames ()
+ public override bool OnDrawAdornments ()
{
return false;
}
diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs
index 469f963de..2ddd8e80e 100644
--- a/Terminal.Gui/Views/Toplevel.cs
+++ b/Terminal.Gui/Views/Toplevel.cs
@@ -560,7 +560,7 @@ public partial class Toplevel : View {
superView = top.SuperView;
}
if (superView.Margin != null && superView == top.SuperView) {
- maxWidth -= superView.GetFramesThickness ().Left + superView.GetFramesThickness ().Right;
+ maxWidth -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
}
if (top.Frame.Width <= maxWidth) {
nx = Math.Max (targetX, 0);
@@ -607,7 +607,7 @@ public partial class Toplevel : View {
maxWidth = statusVisible ? top.SuperView.Frame.Height - 1 : top.SuperView.Frame.Height;
}
if (superView.Margin != null && superView == top.SuperView) {
- maxWidth -= superView.GetFramesThickness ().Top + superView.GetFramesThickness ().Bottom;
+ maxWidth -= superView.GetAdornmentsThickness ().Top + superView.GetAdornmentsThickness ().Bottom;
}
ny = Math.Min (ny, maxWidth);
if (top.Frame.Height <= maxWidth) {
@@ -644,7 +644,7 @@ public partial class Toplevel : View {
bool layoutSubviews = false;
int maxWidth = 0;
if (superView.Margin != null && superView == top.SuperView) {
- maxWidth -= superView.GetFramesThickness ().Left + superView.GetFramesThickness ().Right;
+ maxWidth -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
}
if ((superView != top || top?.SuperView != null || top != Application.Top && top.Modal || top?.SuperView == null && top.IsOverlapped)
// BUGBUG: Prevously PositionToplevel required LayotuStyle.Computed
diff --git a/UICatalog/Properties/launchSettings.json b/UICatalog/Properties/launchSettings.json
index c105de4e1..041a5c22d 100644
--- a/UICatalog/Properties/launchSettings.json
+++ b/UICatalog/Properties/launchSettings.json
@@ -69,6 +69,10 @@
"ListView & ComboBox": {
"commandName": "Project",
"commandLineArgs": "\"ListView & ComboBox\""
+ },
+ "Frames Demo": {
+ "commandName": "Project",
+ "commandLineArgs": "\"Frames Demo\""
}
}
}
\ No newline at end of file
diff --git a/UICatalog/Scenarios/Adornments.cs b/UICatalog/Scenarios/Adornments.cs
new file mode 100644
index 000000000..925a7578b
--- /dev/null
+++ b/UICatalog/Scenarios/Adornments.cs
@@ -0,0 +1,441 @@
+using System;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("Adornments Demo", "Demonstrates Margin, Border, and Padding on Views.")]
+[ScenarioCategory ("Layout")]
+[ScenarioCategory ("Borders")]
+public class Adornments : Scenario {
+
+ public override void Init ()
+ {
+ Application.Init ();
+ ConfigurationManager.Themes.Theme = Theme;
+ ConfigurationManager.Apply ();
+ Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
+
+ var view = new Window { Title = "The Window" };
+ var tf1 = new TextField ("TextField") { Width = 10 };
+ var color = new ColorPicker () { Title = "BG", BoxHeight = 1, BoxWidth =1, X = Pos.AnchorEnd(11) };
+ color.BorderStyle = LineStyle.RoundedDotted;
+ color.ColorChanged += (s, e) => {
+ color.SuperView.ColorScheme = new ColorScheme (color.SuperView.ColorScheme) {
+ Normal = new Attribute(color.SuperView.ColorScheme.Normal.Foreground, e.Color)
+ };
+ };
+
+ var button = new Button ("Press me!") {
+ X = Pos.Center (),
+ Y = Pos.Center ()
+ };
+ button.Clicked += (s, e) => MessageBox.Query (20, 7, "Hi", $"Am I a {view.GetType ().Name}?", "Yes", "No");
+
+ var label = new TextView () {
+ X = Pos.Center (),
+ Y = Pos.Bottom (button),
+ Title = "Title",
+ Text = "I have a 3 row top border.\nMy border inherits from the SuperView.",
+ Width = 40,
+ Height = 6 // TODO: Use Dim.Auto
+ };
+ label.Border.Thickness = new Thickness (1, 3, 1, 1);
+
+ var tf2 = new Button ("Button") {
+ X = Pos.AnchorEnd (10),
+ Y = Pos.AnchorEnd (1),
+ Width = 10
+ };
+ var tv = new Label {
+ Y = Pos.AnchorEnd (3),
+ Width = 25,
+ Height = Dim.Fill (),
+ Text = "Label\nY=AnchorEnd(3),Height=Dim.Fill()"
+ };
+
+ view.Margin.Data = "Margin";
+ view.Margin.Thickness = new Thickness (3);
+
+ view.Border.Data = "Border";
+ view.Border.Thickness = new Thickness (3);
+
+ view.Padding.Data = "Padding";
+ view.Padding.Thickness = new Thickness (3);
+
+ view.Add (tf1, color, button, label, tf2, tv);
+
+ var editor = new AdornmentsEditor {
+ Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
+ ColorScheme = Colors.ColorSchemes [TopLevelColorScheme],
+ };
+ view.X = 36;
+ view.Y = 0;
+ view.Width = Dim.Fill ();
+ view.Height = Dim.Fill ();
+
+ editor.Initialized += (s, e) => {
+ editor.ViewToEdit = view;
+ };
+ //view.Margin.ColorScheme = new ColorScheme (Colors.Dialog);
+ //view.Border.ColorScheme = new ColorScheme (Colors.Error);
+ //view.Padding.ColorScheme = new ColorScheme (Colors.Menu);
+
+ Application.Run (editor);
+ Application.Shutdown ();
+ }
+
+ public override void Run () { }
+
+ public class AdornmentEditor : View {
+ readonly ColorPicker _backgroundColorPicker = new () {
+ Title = "BG",
+ BoxWidth = 1,
+ BoxHeight = 1,
+ BorderStyle = LineStyle.Single,
+ SuperViewRendersLineCanvas = true
+ };
+
+ readonly ColorPicker _foregroundColorPicker = new () {
+ Title = "FG",
+ BoxWidth = 1,
+ BoxHeight = 1,
+ BorderStyle = LineStyle.Single,
+ SuperViewRendersLineCanvas = true
+ };
+
+ TextField _bottomEdit;
+ bool _isUpdating;
+ TextField _leftEdit;
+ TextField _rightEdit;
+ Thickness _thickness;
+ TextField _topEdit;
+
+ public AdornmentEditor ()
+ {
+ Margin.Thickness = new Thickness (0);
+ BorderStyle = LineStyle.Double;
+ Initialized += AdornmentEditor_Initialized;
+ }
+
+ public Attribute Color {
+ get => new (_foregroundColorPicker.SelectedColor, _backgroundColorPicker.SelectedColor);
+ set {
+ _foregroundColorPicker.SelectedColor = value.Foreground.ColorName;
+ _backgroundColorPicker.SelectedColor = value.Background.ColorName;
+ }
+ }
+
+ public Thickness Thickness {
+ get => _thickness;
+ set {
+ if (_isUpdating) {
+ return;
+ }
+ _thickness = value;
+ ThicknessChanged?.Invoke (this, new ThicknessEventArgs { Thickness = Thickness });
+ if (IsInitialized) {
+ _isUpdating = true;
+ if (_topEdit.Text != _thickness.Top.ToString ()) {
+ _topEdit.Text = _thickness.Top.ToString ();
+ }
+ if (_leftEdit.Text != _thickness.Left.ToString ()) {
+ _leftEdit.Text = _thickness.Left.ToString ();
+ }
+ if (_rightEdit.Text != _thickness.Right.ToString ()) {
+ _rightEdit.Text = _thickness.Right.ToString ();
+ }
+ if (_bottomEdit.Text != _thickness.Bottom.ToString ()) {
+ _bottomEdit.Text = _thickness.Bottom.ToString ();
+ }
+ _isUpdating = false;
+ }
+ }
+ }
+
+ public event EventHandler ThicknessChanged;
+
+ public event EventHandler AttributeChanged;
+
+ void AdornmentEditor_Initialized (object sender, EventArgs e)
+ {
+ var editWidth = 3;
+
+ _topEdit = new TextField ("") {
+ X = Pos.Center (),
+ Y = 0,
+ Width = editWidth
+ };
+ _topEdit.TextChanging += Edit_TextChanging;
+ Add (_topEdit);
+
+ _leftEdit = new TextField ("") {
+ X = Pos.Left (_topEdit) - editWidth,
+ Y = Pos.Bottom (_topEdit),
+ Width = editWidth
+ };
+ _leftEdit.TextChanging += Edit_TextChanging;
+ Add (_leftEdit);
+
+ _rightEdit = new TextField ("") {
+ X = Pos.Right (_topEdit),
+ Y = Pos.Bottom (_topEdit),
+ Width = editWidth
+ };
+ _rightEdit.TextChanging += Edit_TextChanging;
+ Add (_rightEdit);
+
+ _bottomEdit = new TextField ("") {
+ X = Pos.Center (),
+ Y = Pos.Bottom (_leftEdit),
+ Width = editWidth
+ };
+ _bottomEdit.TextChanging += Edit_TextChanging;
+ Add (_bottomEdit);
+
+ var copyTop = new Button ("Copy Top") {
+ X = Pos.Center () + 1,
+ Y = Pos.Bottom (_bottomEdit)
+ };
+ copyTop.Clicked += (s, e) => {
+ Thickness = new Thickness (Thickness.Top);
+ if (string.IsNullOrEmpty (_topEdit.Text)) {
+ _topEdit.Text = "0";
+ }
+ _bottomEdit.Text = _leftEdit.Text = _rightEdit.Text = _topEdit.Text;
+ };
+ Add (copyTop);
+
+ // Foreground ColorPicker.
+ _foregroundColorPicker.X = -1;
+ _foregroundColorPicker.Y = Pos.Bottom (copyTop) + 1;
+ _foregroundColorPicker.SelectedColor = Color.Foreground.ColorName;
+ _foregroundColorPicker.ColorChanged += (o, a) =>
+ AttributeChanged?.Invoke (this,
+ new Attribute (_foregroundColorPicker.SelectedColor, _backgroundColorPicker.SelectedColor));
+ Add (_foregroundColorPicker);
+
+ // Background ColorPicker.
+ _backgroundColorPicker.X = Pos.Right (_foregroundColorPicker) - 1;
+ _backgroundColorPicker.Y = Pos.Top (_foregroundColorPicker);
+ _backgroundColorPicker.SelectedColor = Color.Background.ColorName;
+ _backgroundColorPicker.ColorChanged += (o, a) =>
+ AttributeChanged?.Invoke (this,
+ new Attribute (
+ _foregroundColorPicker.SelectedColor,
+ _backgroundColorPicker.SelectedColor));
+ Add (_backgroundColorPicker);
+
+ _topEdit.Text = $"{Thickness.Top}";
+ _leftEdit.Text = $"{Thickness.Left}";
+ _rightEdit.Text = $"{Thickness.Right}";
+ _bottomEdit.Text = $"{Thickness.Bottom}";
+
+ LayoutSubviews ();
+ Height = GetAdornmentsThickness ().Vertical + 4 + 4;
+ Width = GetAdornmentsThickness ().Horizontal + _foregroundColorPicker.Frame.Width * 2 - 3;
+ }
+
+ void Edit_TextChanging (object sender, TextChangingEventArgs e)
+ {
+ try {
+ if (string.IsNullOrEmpty (e.NewText)) {
+ e.Cancel = true;
+ ((TextField)sender).Text = "0";
+ return;
+ }
+ switch (sender.ToString ()) {
+ case var s when s == _topEdit.ToString ():
+ Thickness = new Thickness (Thickness.Left,
+ int.Parse (e.NewText), Thickness.Right,
+ Thickness.Bottom);
+ break;
+ case var s when s == _leftEdit.ToString ():
+ Thickness = new Thickness (int.Parse (e.NewText),
+ Thickness.Top, Thickness.Right,
+ Thickness.Bottom);
+ break;
+ case var s when s == _rightEdit.ToString ():
+ Thickness = new Thickness (Thickness.Left,
+ Thickness.Top, int.Parse (e.NewText),
+ Thickness.Bottom);
+ break;
+ case var s when s == _bottomEdit.ToString ():
+ Thickness = new Thickness (Thickness.Left,
+ Thickness.Top, Thickness.Right,
+ int.Parse (e.NewText));
+ break;
+ }
+ } catch {
+ if (!string.IsNullOrEmpty (e.NewText)) {
+ e.Cancel = true;
+ }
+ }
+ }
+ }
+
+ public class AdornmentsEditor : Window {
+ AdornmentEditor _borderEditor;
+ CheckBox _diagCheckBox;
+ AdornmentEditor _marginEditor;
+ String _origTitle = string.Empty;
+ AdornmentEditor _paddingEditor;
+ View _viewToEdit;
+
+ public View ViewToEdit {
+ get => _viewToEdit;
+ set {
+ _origTitle = value.Title;
+ _viewToEdit = value;
+
+ _marginEditor = new AdornmentEditor {
+ X = 0,
+ Y = 0,
+ Title = "Margin",
+ Thickness = _viewToEdit.Margin.Thickness,
+ Color = new Attribute (_viewToEdit.Margin.ColorScheme.Normal),
+ SuperViewRendersLineCanvas = true
+ };
+ _marginEditor.ThicknessChanged += Editor_ThicknessChanged;
+ _marginEditor.AttributeChanged += Editor_AttributeChanged;
+ Add (_marginEditor);
+
+ _borderEditor = new AdornmentEditor {
+ X = Pos.Left (_marginEditor),
+ Y = Pos.Bottom (_marginEditor),
+ Title = "Border",
+ Thickness = _viewToEdit.Border.Thickness,
+ Color = new Attribute (_viewToEdit.Border.ColorScheme.Normal),
+ SuperViewRendersLineCanvas = true
+ };
+ _borderEditor.ThicknessChanged += Editor_ThicknessChanged;
+ _borderEditor.AttributeChanged += Editor_AttributeChanged;
+ Add (_borderEditor);
+
+
+ var borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast ().ToList ();
+ var rbBorderStyle = new RadioGroup (borderStyleEnum.Select (
+ e => e.ToString ()).ToArray ()) {
+
+ X = Pos.Right (_borderEditor) - 1,
+ Y = Pos.Top (_borderEditor),
+ SelectedItem = (int)_viewToEdit.Border.LineStyle,
+ BorderStyle = LineStyle.Double,
+ Title = "Border Style",
+ SuperViewRendersLineCanvas = true
+ };
+ Add (rbBorderStyle);
+
+ rbBorderStyle.SelectedItemChanged += (s, e) => {
+ var prevBorderStyle = _viewToEdit.BorderStyle;
+ _viewToEdit.Border.LineStyle = (LineStyle)e.SelectedItem;
+ if (_viewToEdit.Border.LineStyle == LineStyle.None) {
+ _viewToEdit.Border.Thickness = new Thickness (0);
+ } else if (prevBorderStyle == LineStyle.None && _viewToEdit.Border.LineStyle != LineStyle.None) {
+ _viewToEdit.Border.Thickness = new Thickness (1);
+ }
+ _borderEditor.Thickness = new Thickness (_viewToEdit.Border.Thickness.Left, _viewToEdit.Border.Thickness.Top,
+ _viewToEdit.Border.Thickness.Right, _viewToEdit.Border.Thickness.Bottom);
+ _viewToEdit.SetNeedsDisplay ();
+ LayoutSubviews ();
+ };
+
+ var ckbTitle = new CheckBox ("Show Title") {
+ BorderStyle = LineStyle.Double,
+ X = Pos.Left (_borderEditor),
+ Y = Pos.Bottom (_borderEditor) - 1,
+ Width = Dim.Width (_borderEditor),
+ Checked = true,
+ SuperViewRendersLineCanvas = true
+ };
+ ckbTitle.Toggled += (sender, args) => {
+ if (ckbTitle.Checked == true) {
+ _viewToEdit.Title = _origTitle;
+ } else {
+ _viewToEdit.Title = string.Empty;
+ }
+ };
+ Add (ckbTitle);
+
+ _paddingEditor = new AdornmentEditor {
+ X = Pos.Left (_borderEditor),
+ Y = Pos.Bottom (rbBorderStyle),
+ Title = "Padding",
+ Thickness = _viewToEdit.Padding.Thickness,
+ Color = new Attribute (_viewToEdit.Padding.ColorScheme.Normal),
+ SuperViewRendersLineCanvas = true
+ };
+ _paddingEditor.ThicknessChanged += Editor_ThicknessChanged;
+ _paddingEditor.AttributeChanged += Editor_AttributeChanged;
+ Add (_paddingEditor);
+
+ _diagCheckBox = new CheckBox {
+ Text = "Diagnostics",
+ Y = Pos.Bottom (_paddingEditor)
+ };
+ _diagCheckBox.Toggled += (s, e) => {
+ if (e.NewValue == true) {
+ ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler;
+ } else {
+ ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.Off;
+ }
+ };
+
+ Add (_diagCheckBox);
+ Add (_viewToEdit);
+
+ _viewToEdit.LayoutComplete += (s, e) => {
+ if (ckbTitle.Checked == true) {
+ _viewToEdit.Title = _origTitle;
+ } else {
+ _viewToEdit.Title = string.Empty;
+ }
+ };
+ }
+ }
+
+ void Editor_AttributeChanged (object sender, Attribute attr)
+ {
+ switch (sender.ToString ()) {
+ case var s when s == _marginEditor.ToString ():
+ _viewToEdit.Margin.ColorScheme = new ColorScheme (_viewToEdit.Margin.ColorScheme) { Normal = attr };
+ break;
+ case var s when s == _borderEditor.ToString ():
+ _viewToEdit.Border.ColorScheme = new ColorScheme (_viewToEdit.Border.ColorScheme) { Normal = attr };
+ break;
+ case var s when s == _paddingEditor.ToString ():
+ _viewToEdit.Padding.ColorScheme = new ColorScheme (_viewToEdit.Padding.ColorScheme) { Normal = attr };
+ break;
+ }
+ }
+
+ void Editor_ThicknessChanged (object sender, ThicknessEventArgs e)
+ {
+ try {
+ switch (sender.ToString ()) {
+ case var s when s == _marginEditor.ToString ():
+ _viewToEdit.Margin.Thickness = e.Thickness;
+ break;
+ case var s when s == _borderEditor.ToString ():
+ _viewToEdit.Border.Thickness = e.Thickness;
+ break;
+ case var s when s == _paddingEditor.ToString ():
+ _viewToEdit.Padding.Thickness = e.Thickness;
+ break;
+ }
+ } catch {
+ switch (sender.ToString ()) {
+ case var s when s == _marginEditor.ToString ():
+ _viewToEdit.Margin.Thickness = e.PreviousThickness;
+ break;
+ case var s when s == _borderEditor.ToString ():
+ _viewToEdit.Border.Thickness = e.PreviousThickness;
+ break;
+ case var s when s == _paddingEditor.ToString ():
+ _viewToEdit.Padding.Thickness = e.PreviousThickness;
+ break;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/UICatalog/Scenarios/DatePickers.cs b/UICatalog/Scenarios/DatePickers.cs
index 5de075ef0..a0a5308bf 100644
--- a/UICatalog/Scenarios/DatePickers.cs
+++ b/UICatalog/Scenarios/DatePickers.cs
@@ -1,5 +1,4 @@
using Terminal.Gui;
-using Terminal.Gui.Views;
namespace UICatalog.Scenarios;
[ScenarioMetadata (Name: "Date Picker", Description: "Demonstrates how to use DatePicker class")]
diff --git a/UICatalog/Scenarios/Frames.cs b/UICatalog/Scenarios/Frames.cs
deleted file mode 100644
index 1b9a313af..000000000
--- a/UICatalog/Scenarios/Frames.cs
+++ /dev/null
@@ -1,399 +0,0 @@
-using System;
-using System.Linq;
-using Terminal.Gui;
-
-namespace UICatalog.Scenarios {
- [ScenarioMetadata (Name: "Frames Demo", Description: "Demonstrates Margin, Border, and Padding on Views.")]
- [ScenarioCategory ("Layout")]
- [ScenarioCategory ("Borders")]
- public class Frames : Scenario {
- public class FrameEditor : View {
- private Thickness _thickness;
- private TextField _topEdit;
- private TextField _leftEdit;
- private TextField _rightEdit;
- private TextField _bottomEdit;
- private bool _isUpdating;
-
- private ColorPicker _foregroundColorPicker;
- private ColorPicker _backgroundColorPicker;
-
- public Terminal.Gui.Attribute Color { get; set; }
-
- public Thickness Thickness {
- get => _thickness;
- set {
- if (_isUpdating) {
- return;
- }
- _thickness = value;
- ThicknessChanged?.Invoke (this, new ThicknessEventArgs () { Thickness = Thickness });
- if (IsInitialized) {
- _isUpdating = true;
- if (_topEdit.Text != _thickness.Top.ToString ()) {
- _topEdit.Text = _thickness.Top.ToString ();
- }
- if (_leftEdit.Text != _thickness.Left.ToString ()) {
- _leftEdit.Text = _thickness.Left.ToString ();
- }
- if (_rightEdit.Text != _thickness.Right.ToString ()) {
- _rightEdit.Text = _thickness.Right.ToString ();
- }
- if (_bottomEdit.Text != _thickness.Bottom.ToString ()) {
- _bottomEdit.Text = _thickness.Bottom.ToString ();
- }
- _isUpdating = false;
- }
- }
- }
-
- public event EventHandler ThicknessChanged;
- public event EventHandler AttributeChanged;
-
- public FrameEditor ()
- {
- Margin.Thickness = new Thickness (0);
- BorderStyle = LineStyle.Double;
- Initialized += FrameEditor_Initialized; ;
- }
-
- void FrameEditor_Initialized (object sender, EventArgs e)
- {
- var editWidth = 3;
-
- _topEdit = new TextField ("") {
- X = Pos.Center (),
- Y = 0,
- Width = editWidth
- };
- _topEdit.TextChanging += Edit_TextChanging;
- Add (_topEdit);
-
- _leftEdit = new TextField ("") {
- X = Pos.Left (_topEdit) - editWidth,
- Y = Pos.Bottom (_topEdit),
- Width = editWidth
- };
- _leftEdit.TextChanging += Edit_TextChanging;
- Add (_leftEdit);
-
- _rightEdit = new TextField ("") {
- X = Pos.Right (_topEdit),
- Y = Pos.Bottom (_topEdit),
- Width = editWidth
- };
- _rightEdit.TextChanging += Edit_TextChanging;
- Add (_rightEdit);
-
- _bottomEdit = new TextField ("") {
- X = Pos.Center (),
- Y = Pos.Bottom (_leftEdit),
- Width = editWidth
- };
- _bottomEdit.TextChanging += Edit_TextChanging;
- Add (_bottomEdit);
-
- var copyTop = new Button ("Copy Top") {
- X = Pos.Center () + 1,
- Y = Pos.Bottom (_bottomEdit)
- };
- copyTop.Clicked += (s, e) => {
- Thickness = new Thickness (Thickness.Top);
- if (string.IsNullOrEmpty (_topEdit.Text)) {
- _topEdit.Text = "0";
- }
- _bottomEdit.Text = _leftEdit.Text = _rightEdit.Text = _topEdit.Text;
- };
- Add (copyTop);
-
- // Foreground ColorPicker.
- _foregroundColorPicker = new ColorPicker () {
- Title = "FG",
- BoxWidth = 1,
- BoxHeight = 1,
- X = -1,
- Y = Pos.Bottom (copyTop) + 1,
- BorderStyle = LineStyle.Single,
- SuperViewRendersLineCanvas = true
- };
- _foregroundColorPicker.ColorChanged += (o, a) =>
- AttributeChanged?.Invoke (this,
- new Attribute (_foregroundColorPicker.SelectedColor, _backgroundColorPicker.SelectedColor));
- Add (_foregroundColorPicker);
-
- // Background ColorPicker.
- _backgroundColorPicker = new ColorPicker () {
- Title = "BG",
- BoxWidth = 1,
- BoxHeight = 1,
- X = Pos.Right (_foregroundColorPicker) - 1,
- Y = Pos.Top (_foregroundColorPicker),
- BorderStyle = LineStyle.Single,
- SuperViewRendersLineCanvas = true
- };
-
- _backgroundColorPicker.ColorChanged += (o, a) =>
- AttributeChanged?.Invoke (this,
- new Terminal.Gui.Attribute (
- _foregroundColorPicker.SelectedColor,
- _backgroundColorPicker.SelectedColor));
- Add (_backgroundColorPicker);
-
- _topEdit.Text = $"{Thickness.Top}";
- _leftEdit.Text = $"{Thickness.Left}";
- _rightEdit.Text = $"{Thickness.Right}";
- _bottomEdit.Text = $"{Thickness.Bottom}";
-
- LayoutSubviews ();
- Height = GetFramesThickness ().Vertical + 4 + 4;
- Width = GetFramesThickness ().Horizontal + _foregroundColorPicker.Frame.Width * 2 - 3;
- }
-
- private void Edit_TextChanging (object sender, TextChangingEventArgs e)
- {
- try {
- if (string.IsNullOrEmpty (e.NewText.ToString ())) {
- e.Cancel = true;
- ((TextField)sender).Text = "0";
- return;
- }
- switch (sender.ToString ()) {
- case var s when s == _topEdit.ToString ():
- Thickness = new Thickness (Thickness.Left,
- int.Parse (e.NewText), Thickness.Right,
- Thickness.Bottom);
- break;
- case var s when s == _leftEdit.ToString ():
- Thickness = new Thickness (int.Parse (e.NewText),
- Thickness.Top, Thickness.Right,
- Thickness.Bottom);
- break;
- case var s when s == _rightEdit.ToString ():
- Thickness = new Thickness (Thickness.Left,
- Thickness.Top, int.Parse (e.NewText),
- Thickness.Bottom);
- break;
- case var s when s == _bottomEdit.ToString ():
- Thickness = new Thickness (Thickness.Left,
- Thickness.Top, Thickness.Right,
- int.Parse (e.NewText));
- break;
- }
- } catch {
- if (!string.IsNullOrEmpty (e.NewText)) {
- e.Cancel = true;
- }
- }
- }
- }
-
- public class FramesEditor : Window {
- private View _viewToEdit;
- private FrameEditor _marginEditor;
- private FrameEditor _borderEditor;
- private FrameEditor _paddingEditor;
- private String _origTitle = string.Empty;
-
- public FramesEditor ()
- {
- }
-
- public View ViewToEdit {
- get {
- return _viewToEdit;
- }
- set {
- _origTitle = value.Title;
- _viewToEdit = value;
-
- _viewToEdit.Margin.ColorScheme = new ColorScheme (Colors.ColorSchemes ["Toplevel"]);
- _marginEditor = new FrameEditor () {
- X = 0,
- Y = 0,
- Title = "Margin",
- Thickness = _viewToEdit.Margin.Thickness,
- SuperViewRendersLineCanvas = true
- };
- _marginEditor.ThicknessChanged += Editor_ThicknessChanged;
- _marginEditor.AttributeChanged += Editor_AttributeChanged;
- Add (_marginEditor);
-
- _viewToEdit.Border.ColorScheme = new ColorScheme (Colors.ColorSchemes ["Base"]);
- _borderEditor = new FrameEditor () {
- X = Pos.Left (_marginEditor),
- Y = Pos.Bottom (_marginEditor),
- Title = "Border",
- Thickness = _viewToEdit.Border.Thickness,
- SuperViewRendersLineCanvas = true
- };
- _borderEditor.ThicknessChanged += Editor_ThicknessChanged;
- _borderEditor.AttributeChanged += Editor_AttributeChanged;
- Add (_borderEditor);
-
- _viewToEdit.Padding.ColorScheme = new ColorScheme (Colors.ColorSchemes ["Error"]);
-
- var borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast ().ToList ();
- var rbBorderStyle = new RadioGroup (borderStyleEnum.Select (
- e => e.ToString ()).ToArray ()) {
-
- X = Pos.Right (_borderEditor) - 1,
- Y = Pos.Top (_borderEditor),
- SelectedItem = (int)_viewToEdit.Border.BorderStyle,
- BorderStyle = LineStyle.Double,
- Title = "Border Style",
- SuperViewRendersLineCanvas = true
- };
- Add (rbBorderStyle);
-
- rbBorderStyle.SelectedItemChanged += (s, e) => {
- var prevBorderStyle = _viewToEdit.BorderStyle;
- _viewToEdit.Border.BorderStyle = (LineStyle)e.SelectedItem;
- if (_viewToEdit.Border.BorderStyle == LineStyle.None) {
- _viewToEdit.Border.Thickness = new Thickness (0);
- } else if (prevBorderStyle == LineStyle.None && _viewToEdit.Border.BorderStyle != LineStyle.None) {
- _viewToEdit.Border.Thickness = new Thickness (1);
- }
- _borderEditor.Thickness = new Thickness (_viewToEdit.Border.Thickness.Left, _viewToEdit.Border.Thickness.Top,
- _viewToEdit.Border.Thickness.Right, _viewToEdit.Border.Thickness.Bottom);
- _viewToEdit.SetNeedsDisplay ();
- LayoutSubviews ();
- };
-
- var ckbTitle = new CheckBox ("Show Title") {
- BorderStyle = LineStyle.Double,
- X = Pos.Left (_borderEditor),
- Y = Pos.Bottom (_borderEditor) - 1,
- Width = Dim.Width (_borderEditor),
- Checked = true,
- SuperViewRendersLineCanvas = true
- };
- ckbTitle.Toggled += (sender, args) => {
- if (ckbTitle.Checked == true) {
- _viewToEdit.Title = _origTitle;
- } else {
- _viewToEdit.Title = string.Empty;
- }
- };
- Add (ckbTitle);
-
- _paddingEditor = new FrameEditor () {
- X = Pos.Left (_borderEditor),
- Y = Pos.Bottom (rbBorderStyle),
- Title = "Padding",
- Thickness = _viewToEdit.Padding.Thickness,
- SuperViewRendersLineCanvas = true
- };
- _paddingEditor.ThicknessChanged += Editor_ThicknessChanged;
- _paddingEditor.AttributeChanged += Editor_AttributeChanged;
- Add (_paddingEditor);
- Add (_viewToEdit);
-
- _viewToEdit.LayoutComplete += (s, e) => {
- if (ckbTitle.Checked == true) {
- _viewToEdit.Title = _origTitle;
- } else {
- _viewToEdit.Title = string.Empty;
- }
- };
- }
- }
-
- private void Editor_AttributeChanged (object sender, Terminal.Gui.Attribute attr)
- {
- switch (sender.ToString ()) {
- case var s when s == _marginEditor.ToString ():
- _viewToEdit.Margin.ColorScheme = new ColorScheme (_viewToEdit.Margin.ColorScheme) { Normal = attr };
- break;
- case var s when s == _borderEditor.ToString ():
- _viewToEdit.Border.ColorScheme = new ColorScheme (_viewToEdit.Border.ColorScheme) { Normal = attr };
- break;
- case var s when s == _paddingEditor.ToString ():
- _viewToEdit.Padding.ColorScheme = new ColorScheme (_viewToEdit.Padding.ColorScheme) { Normal = attr };
- break;
- }
- }
-
- private void Editor_ThicknessChanged (object sender, ThicknessEventArgs e)
- {
- try {
- switch (sender.ToString ()) {
- case var s when s == _marginEditor.ToString ():
- _viewToEdit.Margin.Thickness = e.Thickness;
- break;
- case var s when s == _borderEditor.ToString ():
- _viewToEdit.Border.Thickness = e.Thickness;
- break;
- case var s when s == _paddingEditor.ToString ():
- _viewToEdit.Padding.Thickness = e.Thickness;
- break;
- }
- } catch {
- switch (sender.ToString ()) {
- case var s when s == _marginEditor.ToString ():
- _viewToEdit.Margin.Thickness = e.PreviousThickness;
- break;
- case var s when s == _borderEditor.ToString ():
- _viewToEdit.Border.Thickness = e.PreviousThickness;
- break;
- case var s when s == _paddingEditor.ToString ():
- _viewToEdit.Padding.Thickness = e.PreviousThickness;
- break;
- }
- }
- }
- }
-
- public override void Init ()
- {
- Application.Init ();
- ConfigurationManager.Themes.Theme = Theme;
- ConfigurationManager.Apply ();
- Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
-
- var view = new Window ();
- var tf1 = new TextField ("TextField") { Width = 10 };
-
- var button = new Button ("Press me!") {
- X = Pos.Center (),
- Y = Pos.Center (),
- };
- button.Clicked += (s, e) => MessageBox.Query (20, 7, "Hi", $"Am I a {view.GetType ().Name}?", "Yes", "No");
- var label = new Label ($"I'm a {view.GetType ().Name}") {
- X = Pos.Center (),
- Y = Pos.Center () - 1,
- };
- var tf2 = new Button ("Button") {
- X = Pos.AnchorEnd (10),
- Y = Pos.AnchorEnd (1),
- Width = 10
- };
- var tv = new Label () {
- Y = Pos.AnchorEnd (2),
- Width = 25,
- Height = Dim.Fill (),
- Text = "Label\nY=AnchorEnd(2),Height=Dim.Fill()"
- };
-
- view.Margin.Thickness = new Thickness (3);
- view.Padding.Thickness = new Thickness (1);
-
- view.Add (tf1, button, label, tf2, tv);
-
- var editor = new FramesEditor () {
- Title =$"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
- ViewToEdit = view
- };
- view.X = 36;
- view.Y = 0;
- view.Width = Dim.Fill ();
- view.Height = Dim.Fill ();
-
- Application.Run (editor);
- Application.Shutdown ();
- }
-
- public override void Run ()
- {
- }
- }
-}
\ No newline at end of file
diff --git a/UICatalog/Scenarios/LineDrawing.cs b/UICatalog/Scenarios/LineDrawing.cs
index a4fb8e0a4..694d904d9 100644
--- a/UICatalog/Scenarios/LineDrawing.cs
+++ b/UICatalog/Scenarios/LineDrawing.cs
@@ -55,8 +55,8 @@ namespace UICatalog.Scenarios {
private void ToolsView_Initialized (object sender, EventArgs e)
{
LayoutSubviews ();
- Width = Math.Max (_colorPicker.Frame.Width, _stylePicker.Frame.Width) + GetFramesThickness ().Horizontal;
- Height = _colorPicker.Frame.Height + _stylePicker.Frame.Height + _addLayerBtn.Frame.Height + GetFramesThickness ().Vertical;
+ Width = Math.Max (_colorPicker.Frame.Width, _stylePicker.Frame.Width) + GetAdornmentsThickness ().Horizontal;
+ Height = _colorPicker.Frame.Height + _stylePicker.Frame.Height + _addLayerBtn.Frame.Height + GetAdornmentsThickness ().Vertical;
SuperView.LayoutSubviews ();
}
diff --git a/UICatalog/Scenarios/ProgressBarStyles.cs b/UICatalog/Scenarios/ProgressBarStyles.cs
index 79b77eca6..0f25b7b14 100644
--- a/UICatalog/Scenarios/ProgressBarStyles.cs
+++ b/UICatalog/Scenarios/ProgressBarStyles.cs
@@ -2,7 +2,7 @@
using System.Linq;
using System.Threading;
using Terminal.Gui;
-using static UICatalog.Scenarios.Frames;
+using static UICatalog.Scenarios.Adornments;
namespace UICatalog.Scenarios;
@@ -25,7 +25,7 @@ public class ProgressBarStyles : Scenario {
ConfigurationManager.Themes.Theme = Theme;
ConfigurationManager.Apply ();
- var editor = new FramesEditor {
+ var editor = new AdornmentsEditor {
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
BorderStyle = LineStyle.Single
};
diff --git a/UICatalog/Scenarios/ViewExperiments.cs b/UICatalog/Scenarios/ViewExperiments.cs
index c01d864d8..59cd022b7 100644
--- a/UICatalog/Scenarios/ViewExperiments.cs
+++ b/UICatalog/Scenarios/ViewExperiments.cs
@@ -41,7 +41,7 @@ namespace UICatalog.Scenarios {
view.Margin.ColorScheme = Colors.ColorSchemes ["Toplevel"];
view.Margin.Data = "Margin";
view.Border.Thickness = new Thickness (3);
- view.Border.BorderStyle = LineStyle.Single;
+ view.Border.LineStyle = LineStyle.Single;
view.Border.ColorScheme = view.ColorScheme;
view.Border.Data = "Border";
view.Padding.Thickness = new Thickness (2);
@@ -62,7 +62,7 @@ namespace UICatalog.Scenarios {
window1.Margin.ColorScheme = Colors.ColorSchemes ["Toplevel"];
window1.Margin.Data = "Margin";
window1.Border.Thickness = new Thickness (1);
- window1.Border.BorderStyle = LineStyle.Single;
+ window1.Border.LineStyle = LineStyle.Single;
window1.Border.ColorScheme = view.ColorScheme;
window1.Border.Data = "Border";
window1.Padding.Thickness = new Thickness (0);
@@ -86,7 +86,7 @@ namespace UICatalog.Scenarios {
window2.Margin.ColorScheme = Colors.ColorSchemes ["Toplevel"];
window2.Margin.Data = "Margin";
window2.Border.Thickness = new Thickness (1, 1, 1, 1);
- window2.Border.BorderStyle = LineStyle.Single;
+ window2.Border.LineStyle = LineStyle.Single;
window2.Border.ColorScheme = view.ColorScheme;
window2.Border.Data = "Border";
window2.Padding.Thickness = new Thickness (1, 1, 0, 0);
@@ -110,7 +110,7 @@ namespace UICatalog.Scenarios {
view4.Margin.ColorScheme = Colors.ColorSchemes ["Toplevel"];
view4.Margin.Data = "Margin";
view4.Border.Thickness = new Thickness (1, 1, 1, 1);
- view4.Border.BorderStyle = LineStyle.Single;
+ view4.Border.LineStyle = LineStyle.Single;
view4.Border.ColorScheme = view.ColorScheme;
view4.Border.Data = "Border";
view4.Padding.Thickness = new Thickness (0, 0, 1, 1);
@@ -133,7 +133,7 @@ namespace UICatalog.Scenarios {
view5.Margin.ColorScheme = Colors.ColorSchemes ["Toplevel"];
view5.Margin.Data = "Margin";
view5.Border.Thickness = new Thickness (1, 1, 1, 1);
- view5.Border.BorderStyle = LineStyle.Single;
+ view5.Border.LineStyle = LineStyle.Single;
view5.Border.ColorScheme = view.ColorScheme;
view5.Border.Data = "Border";
view5.Padding.Thickness = new Thickness (0, 0, 0, 0);
@@ -169,23 +169,18 @@ namespace UICatalog.Scenarios {
};
view.Add (edit);
- edit = new TextField () {
+ var label50 = new View () {
+ Title = "Border Inherit Demo",
Text = "Center();50%",
X = Pos.Center (),
Y = Pos.Percent (50),
Width = 30,
- Height = 1
+ TextAlignment = TextAlignment.Centered,
+ //Height = 1
};
- view.Add (edit);
-
- edit = new TextField () {
- Text = "Center() - 1;60%",
- X = Pos.Center () - 1,
- Y = Pos.Percent (60),
- Width = 30,
- Height = 1
- };
- view.Add (edit);
+ label50.Border.Thickness = new Thickness (1, 3, 1, 1);
+ label50.Height = 5;
+ view.Add (label50);
edit = new TextField () {
Text = "0 + Percent(50);70%",
@@ -220,8 +215,8 @@ namespace UICatalog.Scenarios {
view.X = Pos.Center ();
- var editor = new Frames.FramesEditor () {
- Title = $"Frames Editor",
+ var editor = new Adornments.AdornmentsEditor () {
+ Title = $"Adornments Editor",
X = 0,
Y = Pos.Bottom (containerLabel),
Width = Dim.Fill (),
diff --git a/UnitTests/Application/ApplicationTests.cs b/UnitTests/Application/ApplicationTests.cs
index 00c9ae172..fd93680b7 100644
--- a/UnitTests/Application/ApplicationTests.cs
+++ b/UnitTests/Application/ApplicationTests.cs
@@ -489,7 +489,7 @@ public class ApplicationTests {
// 1
Colors.Base.Normal
};
- TestHelpers.AssertDriverColorsAre (@"
+ TestHelpers.AssertDriverAttributesAre (@"
1111100000
1111100000
1111100000
@@ -518,7 +518,7 @@ public class ApplicationTests {
// 1
Colors.Base.Normal
};
- TestHelpers.AssertDriverColorsAre (@"
+ TestHelpers.AssertDriverAttributesAre (@"
0000000000
0111110000
0111110000
diff --git a/UnitTests/Clipboard/ClipboardTests.cs b/UnitTests/Clipboard/ClipboardTests.cs
index 9c1f33d69..d9b186452 100644
--- a/UnitTests/Clipboard/ClipboardTests.cs
+++ b/UnitTests/Clipboard/ClipboardTests.cs
@@ -13,21 +13,21 @@ public class ClipboardTests {
this.output = output;
}
- [Fact] [AutoInitShutdown (useFakeClipboard: true, fakeClipboardAlwaysThrowsNotSupportedException: true)]
+ [Fact, AutoInitShutdown (useFakeClipboard: true, fakeClipboardAlwaysThrowsNotSupportedException: true)]
public void IClipboard_GetClipBoardData_Throws_NotSupportedException ()
{
var iclip = Application.Driver.Clipboard;
Assert.Throws (() => iclip.GetClipboardData ());
}
- [Fact] [AutoInitShutdown (useFakeClipboard: true, fakeClipboardAlwaysThrowsNotSupportedException: true)]
+ [Fact, AutoInitShutdown (useFakeClipboard: true, fakeClipboardAlwaysThrowsNotSupportedException: true)]
public void IClipboard_SetClipBoardData_Throws_NotSupportedException ()
{
var iclip = Application.Driver.Clipboard;
Assert.Throws (() => iclip.SetClipboardData ("foo"));
}
- [Fact] [AutoInitShutdown (useFakeClipboard: true)]
+ [Fact, AutoInitShutdown (useFakeClipboard: true)]
public void Contents_Fake_Gets_Sets ()
{
if (!Clipboard.IsSupported) {
@@ -44,7 +44,7 @@ public class ClipboardTests {
Assert.Equal (clipText, Clipboard.Contents);
}
- [Fact] [AutoInitShutdown (useFakeClipboard: false)]
+ [Fact, AutoInitShutdown (useFakeClipboard: false)]
public void Contents_Gets_Sets ()
{
if (!Clipboard.IsSupported) {
@@ -61,7 +61,7 @@ public class ClipboardTests {
Assert.Equal (clipText, Clipboard.Contents);
}
- [Fact] [AutoInitShutdown (useFakeClipboard: false)]
+ [Fact, AutoInitShutdown (useFakeClipboard: false)]
public void Contents_Gets_Sets_When_IsSupportedFalse ()
{
@@ -79,7 +79,7 @@ public class ClipboardTests {
Assert.Equal (clipText, Clipboard.Contents);
}
- [Fact] [AutoInitShutdown (useFakeClipboard: true)]
+ [Fact, AutoInitShutdown (useFakeClipboard: true)]
public void Contents_Fake_Gets_Sets_When_IsSupportedFalse ()
{
@@ -97,7 +97,7 @@ public class ClipboardTests {
Assert.Equal (clipText, Clipboard.Contents);
}
- [Fact] [AutoInitShutdown (useFakeClipboard: false)]
+ [Fact, AutoInitShutdown (useFakeClipboard: false)]
public void IsSupported_Get ()
{
if (Clipboard.IsSupported) {
@@ -107,7 +107,7 @@ public class ClipboardTests {
}
}
- [Fact] [AutoInitShutdown (useFakeClipboard: false)]
+ [Fact, AutoInitShutdown (useFakeClipboard: false)]
public void TryGetClipboardData_Gets_From_OS_Clipboard ()
{
string clipText = "The TryGetClipboardData_Gets_From_OS_Clipboard unit test pasted this to the OS clipboard.";
@@ -126,7 +126,7 @@ public class ClipboardTests {
}
}
- [Fact] [AutoInitShutdown (useFakeClipboard: false)]
+ [Fact, AutoInitShutdown (useFakeClipboard: false)]
public void TrySetClipboardData_Sets_The_OS_Clipboard ()
{
string clipText = "The TrySetClipboardData_Sets_The_OS_Clipboard unit test pasted this to the OS clipboard.";
diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs
index a2ec1e6a9..bd3e9e70f 100644
--- a/UnitTests/Configuration/ConfigurationMangerTests.cs
+++ b/UnitTests/Configuration/ConfigurationMangerTests.cs
@@ -307,14 +307,14 @@ public class ConfigurationManagerTests {
Settings.Update (stream, "TestConfigurationManagerToJson");
}
- [Fact] [AutoInitShutdown (configLocation: ConfigLocations.None)]
+ [Fact, AutoInitShutdown (configLocation: ConfigLocations.None)]
public void TestConfigurationManagerInitDriver_NoLocations ()
{
}
- [Fact] [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
+ [Fact, AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
public void TestConfigurationManagerInitDriver ()
{
Assert.Equal ("Default", Themes.Theme);
diff --git a/UnitTests/Drawing/AttributeTests.cs b/UnitTests/Drawing/AttributeTests.cs
index 5fbda28e8..3caddd412 100644
--- a/UnitTests/Drawing/AttributeTests.cs
+++ b/UnitTests/Drawing/AttributeTests.cs
@@ -364,7 +364,7 @@ public class AttributeTests {
// Arrange
var foregroundColor = new Color (0, 0, 255);
var backgroundColor = new Color (255, 255, 255);
- var expectedString = $"{foregroundColor},{backgroundColor}";
+ var expectedString = $"[{foregroundColor},{backgroundColor}]";
// Act
var attribute = new Attribute (foregroundColor, backgroundColor);
diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs
index b6a34c581..80d5a7838 100644
--- a/UnitTests/TestHelpers.cs
+++ b/UnitTests/TestHelpers.cs
@@ -1,45 +1,56 @@
-using System;
+using CsvHelper.Configuration.Attributes;
+using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Xunit.Abstractions;
-using Xunit;
-using System.Text.RegularExpressions;
-using System.Reflection;
using System.Diagnostics;
-
-using Attribute = Terminal.Gui.Attribute;
-using Microsoft.VisualStudio.TestPlatform.Utilities;
-using Xunit.Sdk;
using System.Globalization;
using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
namespace Terminal.Gui;
+
// This class enables test functions annotated with the [AutoInitShutdown] attribute to
// automatically call Application.Init at start of the test and Application.Shutdown after the
// test exits.
//
// This is necessary because a) Application is a singleton and Init/Shutdown must be called
// as a pair, and b) all unit test functions should be atomic..
-[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
-public class AutoInitShutdownAttribute : Xunit.Sdk.BeforeAfterTestAttribute {
+[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
+public class AutoInitShutdownAttribute : BeforeAfterTestAttribute {
+ readonly Type _driverType;
+
///
/// Initializes a [AutoInitShutdown] attribute, which determines if/how Application.Init and
/// Application.Shutdown are automatically called Before/After a test runs.
///
/// If true, Application.Init will be called Before the test runs.
/// If true, Application.Shutdown will be called After the test runs.
- /// Determines which ConsoleDriver (FakeDriver, WindowsDriver,
+ ///
+ /// Determines which ConsoleDriver (FakeDriver, WindowsDriver,
/// CursesDriver, NetDriver) will be used when Application.Init is called. If null FakeDriver will be used.
- /// Only valid if is true.
- /// If true, will force the use of .
- /// Only valid if == and is true.
- /// Only valid if is true.
- /// Only valid if == and is true.
- /// Only valid if is true.
- /// Only valid if == and is true.
- /// Determines what config file locations will
- /// load from.
+ /// Only valid if is true.
+ ///
+ ///
+ /// If true, will force the use of .
+ /// Only valid if == and is true.
+ ///
+ ///
+ /// Only valid if is true.
+ /// Only valid if == and is true.
+ ///
+ ///
+ /// Only valid if is true.
+ /// Only valid if == and is true.
+ ///
+ ///
+ /// Determines what config file locations will
+ /// load from.
+ ///
public AutoInitShutdownAttribute (bool autoInit = true,
Type consoleDriverType = null,
bool useFakeClipboard = true,
@@ -57,7 +68,6 @@ public class AutoInitShutdownAttribute : Xunit.Sdk.BeforeAfterTestAttribute {
}
bool AutoInit { get; }
- Type _driverType;
public override void Before (MethodInfo methodUnderTest)
{
@@ -91,12 +101,9 @@ public class AutoInitShutdownAttribute : Xunit.Sdk.BeforeAfterTestAttribute {
}
}
-[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
-public class TestRespondersDisposed : Xunit.Sdk.BeforeAfterTestAttribute {
- public TestRespondersDisposed ()
- {
- CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
- }
+[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
+public class TestRespondersDisposed : BeforeAfterTestAttribute {
+ public TestRespondersDisposed () => CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
public override void Before (MethodInfo methodUnderTest)
{
@@ -117,21 +124,20 @@ public class TestRespondersDisposed : Xunit.Sdk.BeforeAfterTestAttribute {
}
}
-[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
-public class SetupFakeDriverAttribute : Xunit.Sdk.BeforeAfterTestAttribute {
+[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
+public class SetupFakeDriverAttribute : BeforeAfterTestAttribute {
///
- /// Enables test functions annotated with the [SetupFakeDriver] attribute to
- /// set Application.Driver to new FakeDriver().
+ /// Enables test functions annotated with the [SetupFakeDriver] attribute to
+ /// set Application.Driver to new FakeDriver(). The driver is setup with
+ /// 10 rows and columns.
///
- public SetupFakeDriverAttribute ()
- {
- }
+ public SetupFakeDriverAttribute () { }
public override void Before (MethodInfo methodUnderTest)
{
Debug.WriteLine ($"Before: {methodUnderTest.Name}");
Assert.Null (Application.Driver);
- Application.Driver = new FakeDriver ();
+ Application.Driver = new FakeDriver () { Rows = 10, Cols = 10 };
}
public override void After (MethodInfo methodUnderTest)
@@ -144,31 +150,22 @@ public class SetupFakeDriverAttribute : Xunit.Sdk.BeforeAfterTestAttribute {
partial class TestHelpers {
[GeneratedRegex ("\\s+$", RegexOptions.Multiline)]
private static partial Regex TrailingWhiteSpaceRegEx ();
+
[GeneratedRegex ("^\\s+", RegexOptions.Multiline)]
private static partial Regex LeadingWhitespaceRegEx ();
-#pragma warning disable xUnit1013 // Public method should be marked as test
- ///
- /// Asserts that the driver contents match the expected contents, optionally ignoring any trailing whitespace.
- ///
- ///
- ///
- /// The ConsoleDriver to use. If null will be used.
- ///
- public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelper output, ConsoleDriver driver = null, bool ignoreLeadingWhitespace = false)
+ public static string DriverContentsToString (ConsoleDriver driver = null)
{
-#pragma warning restore xUnit1013 // Public method should be marked as test
-
var sb = new StringBuilder ();
driver ??= Application.Driver;
var contents = driver.Contents;
- for (int r = 0; r < driver.Rows; r++) {
- for (int c = 0; c < driver.Cols; c++) {
- Rune rune = contents [r, c].Rune;
- if (rune.DecodeSurrogatePair (out char [] spair)) {
- sb.Append (spair);
+ for (var r = 0; r < driver.Rows; r++) {
+ for (var c = 0; c < driver.Cols; c++) {
+ var rune = contents [r, c].Rune;
+ if (rune.DecodeSurrogatePair (out var sp)) {
+ sb.Append (sp);
} else {
sb.Append ((char)rune.Value);
}
@@ -183,9 +180,25 @@ partial class TestHelpers {
sb.AppendLine ();
}
- var actualLook = sb.ToString ();
+ return sb.ToString ();
+ }
- if (string.Equals (expectedLook, actualLook)) return;
+#pragma warning disable xUnit1013 // Public method should be marked as test
+ ///
+ /// Asserts that the driver contents match the expected contents, optionally ignoring any trailing whitespace.
+ ///
+ ///
+ ///
+ /// The ConsoleDriver to use. If null will be used.
+ ///
+ public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelper output, ConsoleDriver driver = null, bool ignoreLeadingWhitespace = false)
+ {
+#pragma warning restore xUnit1013 // Public method should be marked as test
+ var actualLook = DriverContentsToString (driver);
+
+ if (string.Equals (expectedLook, actualLook)) {
+ return;
+ }
// get rid of trailing whitespace on each line (and leading/trailing whitespace of start/end of full string)
expectedLook = TrailingWhiteSpaceRegEx ().Replace (expectedLook, "").Trim ();
@@ -203,7 +216,7 @@ partial class TestHelpers {
// If test is about to fail show user what things looked like
if (!string.Equals (expectedLook, actualLook)) {
output?.WriteLine ("Expected:" + Environment.NewLine + expectedLook);
- output?.WriteLine ("But Was:" + Environment.NewLine + actualLook);
+ output?.WriteLine (" But Was:" + Environment.NewLine + actualLook);
}
Assert.Equal (expectedLook, actualLook);
@@ -231,13 +244,13 @@ partial class TestHelpers {
for (var r = 0; r < driver.Rows; r++) {
var runes = new List ();
for (var c = 0; c < driver.Cols; c++) {
- Rune rune = contents [r, c].Rune;
+ var rune = contents [r, c].Rune;
if (rune != (Rune)' ') {
if (x == -1) {
x = c;
y = r;
- for (int i = 0; i < c; i++) {
- runes.InsertRange (i, new List () { (Rune)' ' });
+ for (var i = 0; i < c; i++) {
+ runes.InsertRange (i, new List { (Rune)' ' });
}
}
if (rune.GetColumns () > 1) {
@@ -248,25 +261,31 @@ partial class TestHelpers {
}
h = r - y + 1;
}
- if (x > -1) runes.Add (rune);
+ if (x > -1) {
+ runes.Add (rune);
+ }
// See Issue #2616
//foreach (var combMark in contents [r, c].CombiningMarks) {
// runes.Add (combMark);
//}
}
- if (runes.Count > 0) lines.Add (runes);
+ if (runes.Count > 0) {
+ lines.Add (runes);
+ }
}
// Remove unnecessary empty lines
if (lines.Count > 0) {
- for (var r = lines.Count - 1; r > h - 1; r--) lines.RemoveAt (r);
+ for (var r = lines.Count - 1; r > h - 1; r--) {
+ lines.RemoveAt (r);
+ }
}
// Remove trailing whitespace on each line
foreach (var row in lines) {
for (var c = row.Count - 1; c >= 0; c--) {
var rune = row [c];
- if (rune != (Rune)' ' || (row.Sum (x => x.GetColumns ()) == w)) {
+ if (rune != (Rune)' ' || row.Sum (x => x.GetColumns ()) == w) {
break;
}
row.RemoveAt (c);
@@ -274,8 +293,8 @@ partial class TestHelpers {
}
// Convert Rune list to string
- for (int r = 0; r < lines.Count; r++) {
- var line = Terminal.Gui.StringExtensions.ToString (lines [r]).ToString ();
+ for (var r = 0; r < lines.Count; r++) {
+ var line = StringExtensions.ToString (lines [r]);
if (r == lines.Count - 1) {
sb.Append (line);
} else {
@@ -294,11 +313,15 @@ partial class TestHelpers {
actualLook = actualLook.Replace ("\r\n", "\n");
// Remove the first and the last line ending from the expectedLook
- if (expectedLook.StartsWith ("\n")) expectedLook = expectedLook [1..];
- if (expectedLook.EndsWith ("\n")) expectedLook = expectedLook [..^1];
+ if (expectedLook.StartsWith ("\n")) {
+ expectedLook = expectedLook [1..];
+ }
+ if (expectedLook.EndsWith ("\n")) {
+ expectedLook = expectedLook [..^1];
+ }
output?.WriteLine ("Expected:" + Environment.NewLine + expectedLook);
- output?.WriteLine ("But Was:" + Environment.NewLine + actualLook);
+ output?.WriteLine (" But Was:" + Environment.NewLine + actualLook);
Assert.Equal (expectedLook, actualLook);
return new Rect (x > -1 ? x : 0, y > -1 ? y : 0, w > -1 ? w : 0, h > -1 ? h : 0);
@@ -306,48 +329,62 @@ partial class TestHelpers {
#pragma warning disable xUnit1013 // Public method should be marked as test
///
- /// Verifies the console was rendered using the given at the given locations.
- /// Pass a bitmap of indexes into as and the
+ /// Verifies the console was rendered using the given at the given locations.
+ /// Pass a bitmap of indexes into as and the
/// test method will verify those colors were used in the row/col of the console during rendering
///
- /// Numbers between 0 and 9 for each row/col of the console. Must be valid indexes of
+ ///
+ /// Numbers between 0 and 9 for each row/col of the console. Must be valid indexes of
+ ///
+ ///
/// The ConsoleDriver to use. If null will be used.
- ///
- public static void AssertDriverColorsAre (string expectedLook, ConsoleDriver driver = null, params Attribute [] expectedColors)
+ ///
+ public static void AssertDriverAttributesAre (string expectedLook, ConsoleDriver driver = null, params Attribute [] expectedAttribute)
{
#pragma warning restore xUnit1013 // Public method should be marked as test
- if (expectedColors.Length > 10) throw new ArgumentException ("This method only works for UIs that use at most 10 colors");
+ if (expectedAttribute.Length > 10) {
+ throw new ArgumentException ("This method only works for UIs that use at most 10 colors");
+ }
expectedLook = expectedLook.Trim ();
driver ??= Application.Driver;
var contents = driver.Contents;
- var r = 0;
- foreach (var line in expectedLook.Split ('\n').Select (l => l.Trim ())) {
+ var line = 0;
+ foreach (var lineString in expectedLook.Split ('\n').Select (l => l.Trim ())) {
- for (var c = 0; c < line.Length; c++) {
+ for (var c = 0; c < lineString.Length; c++) {
- var val = contents [r, c].Attribute;
+ var val = contents [line, c].Attribute;
- var match = expectedColors.Where (e => e == val).ToList ();
+ var match = expectedAttribute.Where (e => e == val).ToList ();
switch (match.Count) {
case 0:
- throw new Exception ($"Unexpected color {val} was used at row {r} and col {c} (indexes start at 0). Color value was {val} (expected colors were {string.Join (",", expectedColors.Select (c => c.PlatformColor.ToString ()))})");
+ throw new Exception ($"{DriverContentsToString (driver)}\n" +
+ $"Expected Attribute {val} at Contents[{line},{c}] {contents [line, c]}' was not found.\n" +
+ $" Expected: {string.Join (",", expectedAttribute.Select (c => c))}\n" +
+ $" But Was: ");
case > 1:
throw new ArgumentException ($"Bad value for expectedColors, {match.Count} Attributes had the same Value");
}
- var colorUsed = Array.IndexOf (expectedColors, match [0]).ToString () [0];
- var userExpected = line [c];
+ var colorUsed = Array.IndexOf (expectedAttribute, match [0]).ToString () [0];
+ var userExpected = lineString [c];
- if (colorUsed != userExpected) throw new Exception ($"Colors used did not match expected at row {r} and col {c} (indexes start at 0). Color index used was {colorUsed} ({val}) but test expected {userExpected} ({expectedColors [int.Parse (userExpected.ToString ())].PlatformColor}) (these are indexes into the expectedColors array)");
+ if (colorUsed != userExpected) {
+ throw new Exception ($"{DriverContentsToString (driver)}\n" +
+ $"Unexpected Attribute at Contents[{line},{c}] {contents [line, c]}.'\n" +
+ $" Expected: {userExpected} ({expectedAttribute [int.Parse (userExpected.ToString ())]})\n" +
+ $" But Was: {colorUsed} ({val})\n");
+ }
}
- r++;
+ line++;
}
}
+
///
/// Verifies the console used all the when rendering.
/// If one or more of the expected colors are not used then the failure will output both
@@ -392,12 +429,14 @@ partial class TestHelpers {
#pragma warning disable xUnit1013 // Public method should be marked as test
///
- /// Verifies two strings are equivalent. If the assert fails, output will be generated to standard
+ /// Verifies two strings are equivalent. If the assert fails, output will be generated to standard
/// output showing the expected and actual look.
///
///
- /// A string containing the expected look. Newlines should be specified as "\r\n" as
- /// they will be converted to to make tests platform independent.
+ ///
+ /// A string containing the expected look. Newlines should be specified as "\r\n" as
+ /// they will be converted to to make tests platform independent.
+ ///
///
public static void AssertEqual (ITestOutputHelper output, string expectedLook, string actualLook)
{
@@ -407,14 +446,14 @@ partial class TestHelpers {
// If test is about to fail show user what things looked like
if (!string.Equals (expectedLook, actualLook)) {
output?.WriteLine ("Expected:" + Environment.NewLine + expectedLook);
- output?.WriteLine ("But Was:" + Environment.NewLine + actualLook);
+ output?.WriteLine (" But Was:" + Environment.NewLine + actualLook);
}
Assert.Equal (expectedLook, actualLook);
}
#pragma warning restore xUnit1013 // Public method should be marked as test
- private static string ReplaceNewLinesToPlatformSpecific (string toReplace)
+ static string ReplaceNewLinesToPlatformSpecific (string toReplace)
{
var replaced = toReplace;
@@ -431,19 +470,16 @@ partial class TestHelpers {
/// Gets a list of instances of all classes derived from View.
///
/// List of View objects
- public static List GetAllViews ()
- {
- return typeof (View).Assembly.GetTypes ()
- .Where (type => type.IsClass && !type.IsAbstract && type.IsPublic && type.IsSubclassOf (typeof (View)))
- .Select (type => GetTypeInitializer (type, type.GetConstructor (Array.Empty ()))).ToList ();
- }
+ public static List GetAllViews () => typeof (View).Assembly.GetTypes ()
+ .Where (type => type.IsClass && !type.IsAbstract && type.IsPublic && type.IsSubclassOf (typeof (View)))
+ .Select (type => GetTypeInitializer (type, type.GetConstructor (Array.Empty ()))).ToList ();
- private static View GetTypeInitializer (Type type, ConstructorInfo ctor)
+ static View GetTypeInitializer (Type type, ConstructorInfo ctor)
{
View viewType = null;
if (type.IsGenericType && type.IsTypeDefinition) {
- List gTypes = new List ();
+ var gTypes = new List ();
foreach (var args in type.GetGenericArguments ()) {
gTypes.Add (typeof (object));
@@ -453,9 +489,9 @@ partial class TestHelpers {
Assert.IsType (type, (View)Activator.CreateInstance (type));
} else {
- ParameterInfo [] paramsInfo = ctor.GetParameters ();
+ var paramsInfo = ctor.GetParameters ();
Type paramType;
- List