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 pTypes = new List (); + var pTypes = new List (); if (type.IsGenericType) { foreach (var args in type.GetGenericArguments ()) { @@ -491,7 +527,7 @@ partial class TestHelpers { return viewType; } - private static void AddArguments (Type paramType, List pTypes) + static void AddArguments (Type paramType, List pTypes) { if (paramType == typeof (Rect)) { pTypes.Add (Rect.Empty); diff --git a/UnitTests/View/Adornment/AdornmentTests.cs b/UnitTests/View/Adornment/AdornmentTests.cs new file mode 100644 index 000000000..497bf84f8 --- /dev/null +++ b/UnitTests/View/Adornment/AdornmentTests.cs @@ -0,0 +1,140 @@ +using System; +using Xunit; +using Xunit.Abstractions; + +namespace Terminal.Gui.ViewTests; + +public class AdornmentTests { + readonly ITestOutputHelper _output; + + public AdornmentTests (ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void Setting_SuperView_Throws () + { + var adornment = new Adornment (null); + Assert.Throws (() => adornment.SuperView = new View ()); + } + + [Fact] + public void Setting_SuperViewRendersLineCanvas_Throws () + { + var adornment = new Adornment (null); + Assert.Throws (() => adornment.SuperViewRendersLineCanvas = true); + } + + [Fact] + public void Setting_Thickness_Changes_Parent_Bounds () + { + var parent = new View () { + Width = 10, + Height = 10 + }; + parent.BeginInit (); + parent.EndInit (); + + Assert.Equal (new Rect (0, 0, 10, 10), parent.Frame); + Assert.Equal (new Rect (0, 0, 10, 10), parent.Bounds); + + parent.Margin.Thickness = new Thickness (1); + Assert.Equal (new Rect (0, 0, 10, 10), parent.Frame); + Assert.Equal (new Rect (0, 0, 8, 8), parent.Bounds); + } + + [Fact] + public void Setting_Thickness_Raises_ThicknessChanged () + { + var adornment = new Adornment (null); + var super = new View (); + var raised = false; + adornment.ThicknessChanged += (s, e) => { + raised = true; + Assert.Equal (Thickness.Empty, e.PreviousThickness); + Assert.Equal (new Thickness (1, 2, 3, 4), e.Thickness); + Assert.Equal (new Thickness (1, 2, 3, 4), adornment.Thickness); + }; + adornment.Thickness = new Thickness (1, 2, 3, 4); + Assert.True (raised); + } + + [Fact] + public void Setting_Bounds_Throws () + { + var adornment = new Adornment (null); + Assert.Throws (() => adornment.Bounds = new Rect (1, 2, 3, 4)); + } + + [Fact] + public void GetAdornmentsThickness () + { + var view = new View (); + Assert.Equal (Thickness.Empty, view.GetAdornmentsThickness ()); + + view.Margin.Thickness = new Thickness (1); + Assert.Equal (new Thickness (1), view.GetAdornmentsThickness ()); + + view.Border.Thickness = new Thickness (1); + Assert.Equal (new Thickness (2), view.GetAdornmentsThickness ()); + + view.Padding.Thickness = new Thickness (1); + Assert.Equal (new Thickness (3), view.GetAdornmentsThickness ()); + + view.Padding.Thickness = new Thickness (2); + Assert.Equal (new Thickness (4), view.GetAdornmentsThickness ()); + + view.Padding.Thickness = new Thickness (1, 2, 3, 4); + Assert.Equal (new Thickness (3, 4, 5, 6), view.GetAdornmentsThickness ()); + + view.Margin.Thickness = new Thickness (1, 2, 3, 4); + Assert.Equal (new Thickness (3, 5, 7, 9), view.GetAdornmentsThickness ()); + view.Dispose (); + } + + [Fact] + public void FrameToScreen_Uses_Parent_Not_SuperView () + { + var parent = new View () { + X = 1, + Y = 2, + Width = 10, + Height = 10 + }; + + parent.BeginInit (); + parent.EndInit (); + + Assert.Equal (new Rect (1, 2, 10, 10), parent.Frame); + Assert.Equal (new Rect (0, 0, 10, 10), parent.Bounds); + Assert.Equal (new Rect (0, 0, 10, 10), parent.Margin.Frame); + Assert.Equal (new Rect (0, 0, 10, 10), parent.Margin.Bounds); + + Assert.Null (parent.Margin.SuperView); + Assert.Equal (new Rect (1, 2, 10, 10), parent.Margin.FrameToScreen ()); + } + + [Fact] + public void BoundsToScreen_Uses_Parent_Not_SuperView () + { + var parent = new View () { + X = 1, + Y = 2, + Width = 10, + Height = 10 + }; + + parent.BeginInit (); + parent.EndInit (); + + Assert.Equal (new Rect (1, 2, 10, 10), parent.Frame); + Assert.Equal (new Rect (0, 0, 10, 10), parent.Bounds); + Assert.Equal (new Rect (0, 0, 10, 10), parent.Margin.Frame); + Assert.Equal (new Rect (0, 0, 10, 10), parent.Margin.Bounds); + + Assert.Null (parent.Margin.SuperView); + var boundsAsScreen = parent.Margin.BoundsToScreen (new Rect (1, 2, 5, 5)); + Assert.Equal (new Rect (2, 4, 5, 5), boundsAsScreen); + } +} \ No newline at end of file diff --git a/UnitTests/View/Adornment/BorderTests.cs b/UnitTests/View/Adornment/BorderTests.cs new file mode 100644 index 000000000..6b2a24646 --- /dev/null +++ b/UnitTests/View/Adornment/BorderTests.cs @@ -0,0 +1,740 @@ +using Xunit; +using Xunit.Abstractions; + +namespace Terminal.Gui.ViewTests; +public class BorderTests { + readonly ITestOutputHelper _output; + + public BorderTests (ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void View_BorderStyle_Defaults () + { + var view = new View (); + Assert.Equal (LineStyle.None, view.BorderStyle); + Assert.Equal (Thickness.Empty, view.Border.Thickness); + view.Dispose (); + } + + [Fact] + public void View_SetBorderStyle () + { + var view = new View (); + view.BorderStyle = LineStyle.Single; + Assert.Equal (LineStyle.Single, view.BorderStyle); + Assert.Equal (new Thickness (1), view.Border.Thickness); + + view.BorderStyle = LineStyle.Double; + Assert.Equal (LineStyle.Double, view.BorderStyle); + Assert.Equal (new Thickness (1), view.Border.Thickness); + + view.BorderStyle = LineStyle.None; + Assert.Equal (LineStyle.None, view.BorderStyle); + Assert.Equal (Thickness.Empty, view.Border.Thickness); + view.Dispose (); + } + + [Theory, AutoInitShutdown] + [InlineData (0)] + [InlineData (1)] + [InlineData (2)] + [InlineData (3)] + public void Border_With_Title_Size_Height (int height) + { + var win = new Window () { + Title = "1234", + Width = Dim.Fill (), + Height = Dim.Fill () + }; + + var rs = Application.Begin (win); + bool firstIteration = false; + + ((FakeDriver)Application.Driver).SetBufferSize (20, height); + Application.RunIteration (ref rs, ref firstIteration); + var expected = string.Empty; + + switch (height) { + case 0: + //Assert.Equal (new Rect (0, 0, 17, 0), subview.Frame); + expected = @" +"; + break; + case 1: + //Assert.Equal (new Rect (0, 0, 17, 0), subview.Frame); + expected = @" +────────────────────"; + break; + case 2: + //Assert.Equal (new Rect (0, 0, 17, 1), subview.Frame); + expected = @" +┌┤1234├────────────┐ +└──────────────────┘ +"; + break; + case 3: + //Assert.Equal (new Rect (0, 0, 17, 2), subview.Frame); + expected = @" +┌┤1234├────────────┐ +│ │ +└──────────────────┘ +"; + break; + } + _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); + Application.End (rs); + } + + [Theory, AutoInitShutdown] + [InlineData (0)] + [InlineData (1)] + [InlineData (2)] + [InlineData (3)] + [InlineData (4)] + [InlineData (5)] + [InlineData (6)] + [InlineData (7)] + [InlineData (8)] + [InlineData (9)] + [InlineData (10)] + public void Border_With_Title_Size_Width (int width) + { + var win = new Window () { + Title = "1234", + Width = Dim.Fill (), + Height = Dim.Fill () + }; + + var rs = Application.Begin (win); + bool firstIteration = false; + + ((FakeDriver)Application.Driver).SetBufferSize (width, 3); + Application.RunIteration (ref rs, ref firstIteration); + var expected = string.Empty; + + switch (width) { + case 1: + //Assert.Equal (new Rect (0, 0, 17, 0), subview.Frame); + expected = @" +│ +│ +│"; + break; + case 2: + //Assert.Equal (new Rect (0, 0, 17, 1), subview.Frame); + expected = @" +┌┐ +││ +└┘"; + break; + case 3: + //Assert.Equal (new Rect (0, 0, 17, 2), subview.Frame); + expected = @" +┌─┐ +│ │ +└─┘ +"; + break; + case 4: + //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + expected = @" +┌┤├┐ +│ │ +└──┘"; + break; + case 5: + //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + expected = @" +┌┤1├┐ +│ │ +└───┘"; + break; + case 6: + //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + expected = @" +┌┤12├┐ +│ │ +└────┘"; + break; + case 7: + //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + expected = @" +┌┤123├┐ +│ │ +└─────┘"; + break; + case 8: + //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + expected = @" +┌┤1234├┐ +│ │ +└──────┘"; + break; + case 9: + //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + expected = @" +┌┤1234├─┐ +│ │ +└───────┘"; + break; + case 10: + //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + expected = @" +┌┤1234├──┐ +│ │ +└────────┘"; + break; + } + _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); + } + + [Fact, AutoInitShutdown] + public void NoSuperView () + { + var win = new Window () { + Width = Dim.Fill (), + Height = Dim.Fill () + }; + + var rs = Application.Begin (win); + bool firstIteration = false; + + ((FakeDriver)Application.Driver).SetBufferSize (3, 3); + Application.RunIteration (ref rs, ref firstIteration); + var expected = @" +┌─┐ +│ │ +└─┘"; + + _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); + } + + [Fact, AutoInitShutdown] + public void HasSuperView () + { + Application.Top.BorderStyle = LineStyle.Double; + + var frame = new FrameView () { + Width = Dim.Fill (), + Height = Dim.Fill () + }; + + Application.Top.Add (frame); + var rs = Application.Begin (Application.Top); + bool firstIteration = false; + + ((FakeDriver)Application.Driver).SetBufferSize (5, 5); + Application.RunIteration (ref rs, ref firstIteration); + var expected = @" +╔═══╗ +║┌─┐║ +║│ │║ +║└─┘║ +╚═══╝"; + + _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); + Application.End (rs); + } + + [Fact, AutoInitShutdown] + public void HasSuperView_Title () + { + Application.Top.BorderStyle = LineStyle.Double; + + var frame = new FrameView () { + Title = "1234", + Width = Dim.Fill (), + Height = Dim.Fill () + }; + + Application.Top.Add (frame); + var rs = Application.Begin (Application.Top); + bool firstIteration = false; + + ((FakeDriver)Application.Driver).SetBufferSize (10, 4); + Application.RunIteration (ref rs, ref firstIteration); + var expected = @" +╔════════╗ +║┌┤1234├┐║ +║└──────┘║ +╚════════╝"; + + _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); + Application.End (rs); + } + + + [Theory] + [InlineData (0, 0, 0, 1, 1)] + [InlineData (1, 0, 0, 2, 2)] + [InlineData (2, 0, 0, 3, 3)] + [InlineData (1, 1, 0, 3, 2)] + [InlineData (1, 0, 1, 2, 3)] + [InlineData (1, 1, 1, 3, 3)] + [InlineData (1, 10, 10, 12, 12)] + public void FrameToScreen_SuperView_WithBorder (int superOffset, int frameX, int frameY, int expectedScreenX, int expectedScreenY) + { + var super = new View () { + X = superOffset, + Y = superOffset, + Width = 20, + Height = 20, + BorderStyle = LineStyle.Single + }; + + var view = new View () { + X = frameX, + Y = frameY, + Width = 10, + Height = 10 + }; + super.Add (view); + var expected = new Rect (expectedScreenX, expectedScreenY, 10, 10); + var actual = view.FrameToScreen (); + Assert.Equal (expected, actual); + } + + [Theory] + [InlineData (0, 0, 0, 2, 2)] + [InlineData (1, 0, 0, 4, 4)] + [InlineData (2, 0, 0, 6, 6)] + [InlineData (1, 1, 0, 5, 4)] + [InlineData (1, 0, 1, 4, 5)] + [InlineData (1, 1, 1, 5, 5)] + [InlineData (1, 10, 10, 14, 14)] + public void FrameToScreen_NestedSuperView_WithBorder (int superOffset, int frameX, int frameY, int expectedScreenX, int expectedScreenY) + { + var superSuper = new View () { + X = superOffset, + Y = superOffset, + Width = 30, + Height = 30, + BorderStyle = LineStyle.Single + }; + + var super = new View () { + X = superOffset, + Y = superOffset, + Width = 20, + Height = 20, + BorderStyle = LineStyle.Single + }; + superSuper.Add (super); + + var view = new View () { + X = frameX, + Y = frameY, + Width = 10, + Height = 10 + }; + super.Add (view); + var expected = new Rect (expectedScreenX, expectedScreenY, 10, 10); + var actual = view.FrameToScreen (); + Assert.Equal (expected, actual); + } + + + [Theory, AutoInitShutdown] + [InlineData (0)] + [InlineData (1)] + [InlineData (2)] + [InlineData (3)] + [InlineData (4)] + [InlineData (5)] + [InlineData (6)] + [InlineData (7)] + [InlineData (8)] + [InlineData (9)] + [InlineData (10)] + public void Border_With_Title_Border_Double_Thickness_Top_Two_Size_Width (int width) + { + var win = new Window () { + Title = "1234", + Width = Dim.Fill (), + Height = Dim.Fill (), + BorderStyle = LineStyle.Double, + }; + win.Border.Thickness.Top = 2; + + var rs = Application.Begin (win); + bool firstIteration = false; + + ((FakeDriver)Application.Driver).SetBufferSize (width, 4); + Application.RunIteration (ref rs, ref firstIteration); + var expected = string.Empty; + + switch (width) { + case 1: + Assert.Equal (new Rect (0, 0, 1, 4), win.Frame); + expected = @" +║ +║ +║"; + break; + case 2: + Assert.Equal (new Rect (0, 0, 2, 4), win.Frame); + expected = @" +╔╗ +║║ +╚╝"; + break; + case 3: + Assert.Equal (new Rect (0, 0, 3, 4), win.Frame); + expected = @" +╔═╗ +║ ║ +╚═╝"; + break; + case 4: + Assert.Equal (new Rect (0, 0, 4, 4), win.Frame); + expected = @" + ╒╕ +╔╛╘╗ +║ ║ +╚══╝"; + break; + case 5: + Assert.Equal (new Rect (0, 0, 5, 4), win.Frame); + expected = @" + ╒═╕ +╔╛1╘╗ +║ ║ +╚═══╝"; + break; + case 6: + Assert.Equal (new Rect (0, 0, 6, 4), win.Frame); + expected = @" + ╒══╕ +╔╛12╘╗ +║ ║ +╚════╝"; + break; + case 7: + Assert.Equal (new Rect (0, 0, 7, 4), win.Frame); + expected = @" + ╒═══╕ +╔╛123╘╗ +║ ║ +╚═════╝"; + break; + case 8: + Assert.Equal (new Rect (0, 0, 8, 4), win.Frame); + expected = @" + ╒════╕ +╔╛1234╘╗ +║ ║ +╚══════╝"; + break; + case 9: + Assert.Equal (new Rect (0, 0, 9, 4), win.Frame); + expected = @" + ╒════╕ +╔╛1234╘═╗ +║ ║ +╚═══════╝"; + break; + case 10: + Assert.Equal (new Rect (0, 0, 10, 4), win.Frame); + expected = @" + ╒════╕ +╔╛1234╘══╗ +║ ║ +╚════════╝"; + break; + } + _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); + Application.End (rs); + } + + [Theory, AutoInitShutdown] + [InlineData (0)] + [InlineData (1)] + [InlineData (2)] + [InlineData (3)] + [InlineData (4)] + [InlineData (5)] + [InlineData (6)] + [InlineData (7)] + [InlineData (8)] + [InlineData (9)] + [InlineData (10)] + public void Border_With_Title_Border_Double_Thickness_Top_Three_Size_Width (int width) + { + var win = new Window () { + Title = "1234", + Width = Dim.Fill (), + Height = Dim.Fill (), + BorderStyle = LineStyle.Double, + }; + win.Border.Thickness.Top = 3; + + var rs = Application.Begin (win); + bool firstIteration = false; + + ((FakeDriver)Application.Driver).SetBufferSize (width, 4); + Application.RunIteration (ref rs, ref firstIteration); + var expected = string.Empty; + + switch (width) { + case 1: + Assert.Equal (new Rect (0, 0, 1, 4), win.Frame); + expected = @" +║ +║ +║"; + break; + case 2: + Assert.Equal (new Rect (0, 0, 2, 4), win.Frame); + expected = @" +╔╗ +║║ +╚╝"; + break; + case 3: + Assert.Equal (new Rect (0, 0, 3, 4), win.Frame); + expected = @" +╔═╗ +║ ║ +╚═╝"; + break; + case 4: + Assert.Equal (new Rect (0, 0, 4, 4), win.Frame); + expected = @" + ╒╕ +╔╡╞╗ +║╘╛║ +╚══╝"; + break; + case 5: + Assert.Equal (new Rect (0, 0, 5, 4), win.Frame); + expected = @" + ╒═╕ +╔╡1╞╗ +║╘═╛║ +╚═══╝"; + break; + case 6: + Assert.Equal (new Rect (0, 0, 6, 4), win.Frame); + expected = @" + ╒══╕ +╔╡12╞╗ +║╘══╛║ +╚════╝"; + break; + case 7: + Assert.Equal (new Rect (0, 0, 7, 4), win.Frame); + expected = @" + ╒═══╕ +╔╡123╞╗ +║╘═══╛║ +╚═════╝"; + break; + case 8: + Assert.Equal (new Rect (0, 0, 8, 4), win.Frame); + expected = @" + ╒════╕ +╔╡1234╞╗ +║╘════╛║ +╚══════╝"; + break; + case 9: + Assert.Equal (new Rect (0, 0, 9, 4), win.Frame); + expected = @" + ╒════╕ +╔╡1234╞═╗ +║╘════╛ ║ +╚═══════╝"; + break; + case 10: + Assert.Equal (new Rect (0, 0, 10, 4), win.Frame); + expected = @" + ╒════╕ +╔╡1234╞══╗ +║╘════╛ ║ +╚════════╝"; + break; + } + _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); + Application.End (rs); + } + + [Theory, AutoInitShutdown] + [InlineData (0)] + [InlineData (1)] + [InlineData (2)] + [InlineData (3)] + [InlineData (4)] + [InlineData (5)] + [InlineData (6)] + [InlineData (7)] + [InlineData (8)] + [InlineData (9)] + [InlineData (10)] + public void Border_With_Title_Border_Double_Thickness_Top_Four_Size_Width (int width) + { + var win = new Window () { + Title = "1234", + Width = Dim.Fill (), + Height = Dim.Fill (), + BorderStyle = LineStyle.Double, + }; + win.Border.Thickness.Top = 4; + + var rs = Application.Begin (win); + bool firstIteration = false; + + ((FakeDriver)Application.Driver).SetBufferSize (width, 5); + Application.RunIteration (ref rs, ref firstIteration); + var expected = string.Empty; + + switch (width) { + case 1: + Assert.Equal (new Rect (0, 0, 1, 5), win.Frame); + expected = @" +║ +║ +║"; + break; + case 2: + Assert.Equal (new Rect (0, 0, 2, 5), win.Frame); + expected = @" +╔╗ +║║ +╚╝"; + break; + case 3: + Assert.Equal (new Rect (0, 0, 3, 5), win.Frame); + expected = @" +╔═╗ +║ ║ +╚═╝"; + break; + case 4: + Assert.Equal (new Rect (0, 0, 4, 5), win.Frame); + expected = @" + ╒╕ +╔╡╞╗ +║╘╛║ +╚══╝"; + break; + case 5: + Assert.Equal (new Rect (0, 0, 5, 5), win.Frame); + expected = @" + ╒═╕ +╔╡1╞╗ +║╘═╛║ +╚═══╝"; + break; + case 6: + Assert.Equal (new Rect (0, 0, 6, 5), win.Frame); + expected = @" + ╒══╕ +╔╡12╞╗ +║╘══╛║ +╚════╝"; + break; + case 7: + Assert.Equal (new Rect (0, 0, 7, 5), win.Frame); + expected = @" + ╒═══╕ +╔╡123╞╗ +║╘═══╛║ +╚═════╝"; + break; + case 8: + Assert.Equal (new Rect (0, 0, 8, 5), win.Frame); + expected = @" + ╒════╕ +╔╡1234╞╗ +║╘════╛║ +╚══════╝"; + break; + case 9: + Assert.Equal (new Rect (0, 0, 9, 5), win.Frame); + expected = @" + ╒════╕ +╔╡1234╞═╗ +║╘════╛ ║ +╚═══════╝"; + break; + case 10: + Assert.Equal (new Rect (0, 0, 10, 5), win.Frame); + expected = @" + ╒════╕ +╔╡1234╞══╗ +║╘════╛ ║ +╚════════╝"; + break; + } + _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); + Application.End (rs); + } + + [Fact, SetupFakeDriver] + public void Border_Uses_Parent_ColorScheme () + { + var view = new View () { + Title = "A", + Height = 2, + Width = 5 + }; + view.Border.Thickness = new Thickness (0, 1, 0, 0); + view.Border.LineStyle = LineStyle.Single; + view.ColorScheme = new ColorScheme () { + Normal = new Attribute (Color.Red, Color.Green), + Focus = new Attribute (Color.Green, Color.Red), + }; + Assert.Equal (Colors.Error.Normal.Foreground, view.ColorScheme.Normal.Foreground); + Assert.Equal (ColorName.Red, view.Border.GetNormalColor ().Foreground.ColorName); + Assert.Equal (ColorName.Green, view.Border.GetFocusColor ().Foreground.ColorName); + Assert.Equal (view.GetNormalColor(), view.Border.GetNormalColor ()); + Assert.Equal (view.GetFocusColor (), view.Border.GetFocusColor ()); + + view.BeginInit (); + view.EndInit (); + view.Draw (); + + var expected = @"─┤A├─"; + TestHelpers.AssertDriverContentsAre (expected, _output); + TestHelpers.AssertDriverAttributesAre ("00000", null, view.ColorScheme.Normal); + } + + [Fact, SetupFakeDriver] + public void Border_Parent_HasFocus_Title_Uses_FocusAttribute () + { + var view = new View () { + Title = "A", + Height = 2, + Width = 5 + }; + view.Border.Thickness = new Thickness (0, 1, 0, 0); + view.Border.LineStyle = LineStyle.Single; + view.ColorScheme = new ColorScheme () { + Normal = new Attribute (Color.Red, Color.Green), + Focus = new Attribute (Color.Green, Color.Red), + }; + Assert.NotEqual (view.ColorScheme.Normal.Foreground, view.ColorScheme.Focus.Foreground); + Assert.Equal (ColorName.Red, view.Border.GetNormalColor ().Foreground.ColorName); + Assert.Equal (ColorName.Green, view.Border.GetFocusColor ().Foreground.ColorName); + Assert.Equal (view.GetFocusColor (), view.Border.GetFocusColor ()); + + view.BeginInit (); + view.EndInit (); + view.Draw (); + + var expected = @"─┤A├─"; + TestHelpers.AssertDriverContentsAre (expected, _output); + TestHelpers.AssertDriverAttributesAre ("00000", null, view.ColorScheme.Normal); + + view.CanFocus = true; + view.SetFocus (); + view.Draw (); + Assert.Equal (view.GetFocusColor (), view.Border.GetFocusColor ()); + Assert.Equal (view.ColorScheme.Focus.Foreground, view.Border.GetFocusColor ().Foreground); + Assert.Equal (view.ColorScheme.Normal.Foreground, view.Border.GetNormalColor ().Foreground); + TestHelpers.AssertDriverAttributesAre ("00100", null, view.ColorScheme.Normal, view.GetFocusColor ()); + } +} diff --git a/UnitTests/View/Adornment/MarginTests.cs b/UnitTests/View/Adornment/MarginTests.cs new file mode 100644 index 000000000..f55fceff7 --- /dev/null +++ b/UnitTests/View/Adornment/MarginTests.cs @@ -0,0 +1,48 @@ +using Xunit; +using Xunit.Abstractions; + +namespace Terminal.Gui.ViewTests; +public class MarginTests { + readonly ITestOutputHelper _output; + + public MarginTests (ITestOutputHelper output) + { + _output = output; + } + + [Fact, SetupFakeDriver] + public void Margin_Uses_SuperView_ColorScheme () + { + ((FakeDriver)Application.Driver).SetBufferSize (5, 5); + var view = new View () { + Height = 3, + Width = 3 + }; + view.Margin.Thickness = new Thickness (1); + + var superView = new View (); + + superView.ColorScheme = new ColorScheme () { + Normal = new Attribute (Color.Red, Color.Green), + Focus = new Attribute (Color.Green, Color.Red), + }; + + superView.Add (view); + Assert.Equal (ColorName.Red, view.Margin.GetNormalColor ().Foreground.ColorName); + Assert.Equal (ColorName.Red, superView.GetNormalColor ().Foreground.ColorName); + Assert.Equal (superView.GetNormalColor (), view.Margin.GetNormalColor ()); + Assert.Equal (superView.GetFocusColor (), view.Margin.GetFocusColor ()); + + superView.BeginInit (); + superView.EndInit (); + ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.FramePadding; + view.Draw (); + ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.Off; + + TestHelpers.AssertDriverContentsAre (@" +LTR +L R +BBB", _output); + TestHelpers.AssertDriverAttributesAre ("0", null, superView.GetNormalColor ()); + } +} diff --git a/UnitTests/View/Adornment/PaddingTests.cs b/UnitTests/View/Adornment/PaddingTests.cs new file mode 100644 index 000000000..e400af340 --- /dev/null +++ b/UnitTests/View/Adornment/PaddingTests.cs @@ -0,0 +1,43 @@ +using Xunit; +using Xunit.Abstractions; + +namespace Terminal.Gui.ViewTests; +public class PaddingTests { + readonly ITestOutputHelper _output; + + public PaddingTests (ITestOutputHelper output) + { + _output = output; + } + + [Fact, SetupFakeDriver] + public void Padding_Uses_Parent_ColorScheme () + { + ((FakeDriver)Application.Driver).SetBufferSize (5, 5); + var view = new View () { + Height = 3, + Width = 3 + }; + view.Padding.Thickness = new Thickness (1); + + view.ColorScheme = new ColorScheme () { + Normal = new Attribute (Color.Red, Color.Green), + Focus = new Attribute (Color.Green, Color.Red), + }; + + Assert.Equal (ColorName.Red, view.Padding.GetNormalColor ().Foreground.ColorName); + Assert.Equal (view.GetNormalColor (), view.Padding.GetNormalColor ()); + + view.BeginInit (); + view.EndInit (); + ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.FramePadding; + view.Draw (); + ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.Off; + + TestHelpers.AssertDriverContentsAre (@" +LTR +L R +BBB", _output); + TestHelpers.AssertDriverAttributesAre ("0", null, view.GetNormalColor ()); + } +} diff --git a/UnitTests/View/BorderTests.cs b/UnitTests/View/BorderTests.cs deleted file mode 100644 index 5a0b59b54..000000000 --- a/UnitTests/View/BorderTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Xunit; -using Xunit.Abstractions; - -namespace Terminal.Gui.ViewTests; -public class BorderTests { - readonly ITestOutputHelper _output; - - public BorderTests (ITestOutputHelper output) - { - this._output = output; - } - - [Fact] - public void View_BorderStyle_Defaults () - { - var view = new View (); - Assert.Equal (LineStyle.None, view.BorderStyle); - Assert.Equal (Thickness.Empty, view.Border.Thickness); - view.Dispose (); - } - - [Fact] - public void View_SetBorderStyle () - { - var view = new View (); - view.BorderStyle = LineStyle.Single; - Assert.Equal (LineStyle.Single, view.BorderStyle); - Assert.Equal (new Thickness (1), view.Border.Thickness); - - view.BorderStyle = LineStyle.Double; - Assert.Equal (LineStyle.Double, view.BorderStyle); - Assert.Equal (new Thickness (1), view.Border.Thickness); - - view.BorderStyle = LineStyle.None; - Assert.Equal (LineStyle.None, view.BorderStyle); - Assert.Equal (Thickness.Empty, view.Border.Thickness); - view.Dispose (); - } - - //[Fact] - //public void View_BorderStyleChanged () - //{ - //} -} \ No newline at end of file diff --git a/UnitTests/View/DrawTests.cs b/UnitTests/View/DrawTests.cs index d41803d9d..70f283184 100644 --- a/UnitTests/View/DrawTests.cs +++ b/UnitTests/View/DrawTests.cs @@ -99,8 +99,8 @@ public class DrawTests { Colors.Base.HotNormal }; - TestHelpers.AssertDriverColorsAre (@" -0020000000 + TestHelpers.AssertDriverAttributesAre (@" +0010000000 0000000000 0111000000 0000000000", Application.Driver, expectedColors); @@ -148,8 +148,8 @@ public class DrawTests { Colors.Base.HotNormal }; - TestHelpers.AssertDriverColorsAre (@" -0022000000 + TestHelpers.AssertDriverAttributesAre (@" +0011000000 0000000000 0111000000 0000000000", Application.Driver, expectedColors); @@ -187,7 +187,7 @@ e s t ", _output); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 000000 0 0 diff --git a/UnitTests/View/FrameTests.cs b/UnitTests/View/FrameTests.cs index fbb33b747..551b44f5d 100644 --- a/UnitTests/View/FrameTests.cs +++ b/UnitTests/View/FrameTests.cs @@ -7,7 +7,7 @@ public class FrameTests { public FrameTests (ITestOutputHelper output) { - this._output = output; + _output = output; } // Test FrameToScreen @@ -17,7 +17,7 @@ public class FrameTests { [InlineData (0, 1, 0, 1)] [InlineData (1, 1, 1, 1)] [InlineData (10, 10, 10, 10)] - void FrameToScreen_NoSuperView (int frameX, int frameY, int expectedScreenX, int expectedScreenY) + public void FrameToScreen_NoSuperView (int frameX, int frameY, int expectedScreenX, int expectedScreenY) { var view = new View () { X = frameX, @@ -38,7 +38,7 @@ public class FrameTests { [InlineData (1, 0, 1, 1, 2)] [InlineData (1, 1, 1, 2, 2)] [InlineData (1, 10, 10, 11, 11)] - void FrameToScreen_SuperView (int superOffset, int frameX, int frameY, int expectedScreenX, int expectedScreenY) + public void FrameToScreen_SuperView (int superOffset, int frameX, int frameY, int expectedScreenX, int expectedScreenY) { var super = new View() { X = superOffset, @@ -58,665 +58,4 @@ public class FrameTests { var actual = view.FrameToScreen (); Assert.Equal (expected, actual); } - - [Theory] - [InlineData (0, 0, 0, 1, 1)] - [InlineData (1, 0, 0, 2, 2)] - [InlineData (2, 0, 0, 3, 3)] - [InlineData (1, 1, 0, 3, 2)] - [InlineData (1, 0, 1, 2, 3)] - [InlineData (1, 1, 1, 3, 3)] - [InlineData (1, 10, 10, 12, 12)] - void FrameToScreen_SuperView_WithBorder (int superOffset, int frameX, int frameY, int expectedScreenX, int expectedScreenY) - { - var super = new View () { - X = superOffset, - Y = superOffset, - Width = 20, - Height = 20, - BorderStyle = LineStyle.Single - }; - - var view = new View () { - X = frameX, - Y = frameY, - Width = 10, - Height = 10 - }; - super.Add (view); - var expected = new Rect (expectedScreenX, expectedScreenY, 10, 10); - var actual = view.FrameToScreen (); - Assert.Equal (expected, actual); - } - - [Theory] - [InlineData (0, 0, 0, 2, 2)] - [InlineData (1, 0, 0, 4, 4)] - [InlineData (2, 0, 0, 6, 6)] - [InlineData (1, 1, 0, 5, 4)] - [InlineData (1, 0, 1, 4, 5)] - [InlineData (1, 1, 1, 5, 5)] - [InlineData (1, 10, 10, 14, 14)] - void FrameToScreen_NestedSuperView_WithBorder (int superOffset, int frameX, int frameY, int expectedScreenX, int expectedScreenY) - { - var superSuper = new View () { - X = superOffset, - Y = superOffset, - Width = 30, - Height = 30, - BorderStyle = LineStyle.Single - }; - - var super = new View () { - X = superOffset, - Y = superOffset, - Width = 20, - Height = 20, - BorderStyle = LineStyle.Single - }; - superSuper.Add (super); - - var view = new View () { - X = frameX, - Y = frameY, - Width = 10, - Height = 10 - }; - super.Add (view); - var expected = new Rect (expectedScreenX, expectedScreenY, 10, 10); - var actual = view.FrameToScreen (); - Assert.Equal (expected, actual); - } - - - [Fact] - public void GetFramesThickness () - { - var view = new View (); - Assert.Equal (Thickness.Empty, view.GetFramesThickness ()); - - view.Margin.Thickness = new Thickness (1); - Assert.Equal (new Thickness (1), view.GetFramesThickness ()); - - view.Border.Thickness = new Thickness (1); - Assert.Equal (new Thickness (2), view.GetFramesThickness ()); - - view.Padding.Thickness = new Thickness (1); - Assert.Equal (new Thickness (3), view.GetFramesThickness ()); - - view.Padding.Thickness = new Thickness (2); - Assert.Equal (new Thickness (4), view.GetFramesThickness ()); - - view.Padding.Thickness = new Thickness (1, 2, 3, 4); - Assert.Equal (new Thickness (3, 4, 5, 6), view.GetFramesThickness ()); - - view.Margin.Thickness = new Thickness (1, 2, 3, 4); - Assert.Equal (new Thickness (3, 5, 7, 9), view.GetFramesThickness ()); - view.Dispose (); - } - - [Theory, AutoInitShutdown] - [InlineData (0)] - [InlineData (1)] - [InlineData (2)] - [InlineData (3)] - public void Border_With_Title_Size_Height (int height) - { - var win = new Window () { - Title = "1234", - Width = Dim.Fill (), - Height = Dim.Fill () - }; - - var rs = Application.Begin (win); - bool firstIteration = false; - - ((FakeDriver)Application.Driver).SetBufferSize (20, height); - Application.RunIteration (ref rs, ref firstIteration); - var expected = string.Empty; - - switch (height) { - case 0: - //Assert.Equal (new Rect (0, 0, 17, 0), subview.Frame); - expected = @" -"; - break; - case 1: - //Assert.Equal (new Rect (0, 0, 17, 0), subview.Frame); - expected = @" -────────────────────"; - break; - case 2: - //Assert.Equal (new Rect (0, 0, 17, 1), subview.Frame); - expected = @" -┌┤1234├────────────┐ -└──────────────────┘ -"; - break; - case 3: - //Assert.Equal (new Rect (0, 0, 17, 2), subview.Frame); - expected = @" -┌┤1234├────────────┐ -│ │ -└──────────────────┘ -"; - break; - } - _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); - Application.End (rs); - } - - [Theory, AutoInitShutdown] - [InlineData (0)] - [InlineData (1)] - [InlineData (2)] - [InlineData (3)] - [InlineData (4)] - [InlineData (5)] - [InlineData (6)] - [InlineData (7)] - [InlineData (8)] - [InlineData (9)] - [InlineData (10)] - public void Border_With_Title_Size_Width (int width) - { - var win = new Window () { - Title = "1234", - Width = Dim.Fill (), - Height = Dim.Fill () - }; - - var rs = Application.Begin (win); - bool firstIteration = false; - - ((FakeDriver)Application.Driver).SetBufferSize (width, 3); - Application.RunIteration (ref rs, ref firstIteration); - var expected = string.Empty; - - switch (width) { - case 1: - //Assert.Equal (new Rect (0, 0, 17, 0), subview.Frame); - expected = @" -│ -│ -│"; - break; - case 2: - //Assert.Equal (new Rect (0, 0, 17, 1), subview.Frame); - expected = @" -┌┐ -││ -└┘"; - break; - case 3: - //Assert.Equal (new Rect (0, 0, 17, 2), subview.Frame); - expected = @" -┌─┐ -│ │ -└─┘ -"; - break; - case 4: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); - expected = @" -┌┤├┐ -│ │ -└──┘"; - break; - case 5: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); - expected = @" -┌┤1├┐ -│ │ -└───┘"; - break; - case 6: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); - expected = @" -┌┤12├┐ -│ │ -└────┘"; - break; - case 7: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); - expected = @" -┌┤123├┐ -│ │ -└─────┘"; - break; - case 8: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); - expected = @" -┌┤1234├┐ -│ │ -└──────┘"; - break; - case 9: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); - expected = @" -┌┤1234├─┐ -│ │ -└───────┘"; - break; - case 10: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); - expected = @" -┌┤1234├──┐ -│ │ -└────────┘"; - break; - } - _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); - } - - [Fact, AutoInitShutdown] - public void NoSuperView () - { - var win = new Window () { - Width = Dim.Fill (), - Height = Dim.Fill () - }; - - var rs = Application.Begin (win); - bool firstIteration = false; - - ((FakeDriver)Application.Driver).SetBufferSize (3, 3); - Application.RunIteration (ref rs, ref firstIteration); - var expected = @" -┌─┐ -│ │ -└─┘"; - - _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); - } - - [Fact, AutoInitShutdown] - public void HasSuperView () - { - Application.Top.BorderStyle = LineStyle.Double; - - var frame = new FrameView () { - Width = Dim.Fill (), - Height = Dim.Fill () - }; - - Application.Top.Add (frame); - var rs = Application.Begin (Application.Top); - bool firstIteration = false; - - ((FakeDriver)Application.Driver).SetBufferSize (5, 5); - Application.RunIteration (ref rs, ref firstIteration); - var expected = @" -╔═══╗ -║┌─┐║ -║│ │║ -║└─┘║ -╚═══╝"; - - _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); - Application.End (rs); - } - - [Fact, AutoInitShutdown] - public void HasSuperView_Title () - { - Application.Top.BorderStyle = LineStyle.Double; - - var frame = new FrameView () { - Title = "1234", - Width = Dim.Fill (), - Height = Dim.Fill () - }; - - Application.Top.Add (frame); - var rs = Application.Begin (Application.Top); - bool firstIteration = false; - - ((FakeDriver)Application.Driver).SetBufferSize (10, 4); - Application.RunIteration (ref rs, ref firstIteration); - var expected = @" -╔════════╗ -║┌┤1234├┐║ -║└──────┘║ -╚════════╝"; - - _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); - Application.End (rs); - } - - [Theory, AutoInitShutdown] - [InlineData (0)] - [InlineData (1)] - [InlineData (2)] - [InlineData (3)] - [InlineData (4)] - [InlineData (5)] - [InlineData (6)] - [InlineData (7)] - [InlineData (8)] - [InlineData (9)] - [InlineData (10)] - public void Border_With_Title_Border_Double_Thickness_Top_Two_Size_Width (int width) - { - var win = new Window () { - Title = "1234", - Width = Dim.Fill (), - Height = Dim.Fill (), - BorderStyle = LineStyle.Double, - }; - win.Border.Thickness.Top = 2; - - var rs = Application.Begin (win); - bool firstIteration = false; - - ((FakeDriver)Application.Driver).SetBufferSize (width, 4); - Application.RunIteration (ref rs, ref firstIteration); - var expected = string.Empty; - - switch (width) { - case 1: - Assert.Equal (new Rect (0, 0, 1, 4), win.Frame); - expected = @" -║ -║ -║"; - break; - case 2: - Assert.Equal (new Rect (0, 0, 2, 4), win.Frame); - expected = @" -╔╗ -║║ -╚╝"; - break; - case 3: - Assert.Equal (new Rect (0, 0, 3, 4), win.Frame); - expected = @" -╔═╗ -║ ║ -╚═╝"; - break; - case 4: - Assert.Equal (new Rect (0, 0, 4, 4), win.Frame); - expected = @" - ╒╕ -╔╛╘╗ -║ ║ -╚══╝"; - break; - case 5: - Assert.Equal (new Rect (0, 0, 5, 4), win.Frame); - expected = @" - ╒═╕ -╔╛1╘╗ -║ ║ -╚═══╝"; - break; - case 6: - Assert.Equal (new Rect (0, 0, 6, 4), win.Frame); - expected = @" - ╒══╕ -╔╛12╘╗ -║ ║ -╚════╝"; - break; - case 7: - Assert.Equal (new Rect (0, 0, 7, 4), win.Frame); - expected = @" - ╒═══╕ -╔╛123╘╗ -║ ║ -╚═════╝"; - break; - case 8: - Assert.Equal (new Rect (0, 0, 8, 4), win.Frame); - expected = @" - ╒════╕ -╔╛1234╘╗ -║ ║ -╚══════╝"; - break; - case 9: - Assert.Equal (new Rect (0, 0, 9, 4), win.Frame); - expected = @" - ╒════╕ -╔╛1234╘═╗ -║ ║ -╚═══════╝"; - break; - case 10: - Assert.Equal (new Rect (0, 0, 10, 4), win.Frame); - expected = @" - ╒════╕ -╔╛1234╘══╗ -║ ║ -╚════════╝"; - break; - } - _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); - Application.End (rs); - } - - [Theory, AutoInitShutdown] - [InlineData (0)] - [InlineData (1)] - [InlineData (2)] - [InlineData (3)] - [InlineData (4)] - [InlineData (5)] - [InlineData (6)] - [InlineData (7)] - [InlineData (8)] - [InlineData (9)] - [InlineData (10)] - public void Border_With_Title_Border_Double_Thickness_Top_Three_Size_Width (int width) - { - var win = new Window () { - Title = "1234", - Width = Dim.Fill (), - Height = Dim.Fill (), - BorderStyle = LineStyle.Double, - }; - win.Border.Thickness.Top = 3; - - var rs = Application.Begin (win); - bool firstIteration = false; - - ((FakeDriver)Application.Driver).SetBufferSize (width, 4); - Application.RunIteration (ref rs, ref firstIteration); - var expected = string.Empty; - - switch (width) { - case 1: - Assert.Equal (new Rect (0, 0, 1, 4), win.Frame); - expected = @" -║ -║ -║"; - break; - case 2: - Assert.Equal (new Rect (0, 0, 2, 4), win.Frame); - expected = @" -╔╗ -║║ -╚╝"; - break; - case 3: - Assert.Equal (new Rect (0, 0, 3, 4), win.Frame); - expected = @" -╔═╗ -║ ║ -╚═╝"; - break; - case 4: - Assert.Equal (new Rect (0, 0, 4, 4), win.Frame); - expected = @" - ╒╕ -╔╡╞╗ -║╘╛║ -╚══╝"; - break; - case 5: - Assert.Equal (new Rect (0, 0, 5, 4), win.Frame); - expected = @" - ╒═╕ -╔╡1╞╗ -║╘═╛║ -╚═══╝"; - break; - case 6: - Assert.Equal (new Rect (0, 0, 6, 4), win.Frame); - expected = @" - ╒══╕ -╔╡12╞╗ -║╘══╛║ -╚════╝"; - break; - case 7: - Assert.Equal (new Rect (0, 0, 7, 4), win.Frame); - expected = @" - ╒═══╕ -╔╡123╞╗ -║╘═══╛║ -╚═════╝"; - break; - case 8: - Assert.Equal (new Rect (0, 0, 8, 4), win.Frame); - expected = @" - ╒════╕ -╔╡1234╞╗ -║╘════╛║ -╚══════╝"; - break; - case 9: - Assert.Equal (new Rect (0, 0, 9, 4), win.Frame); - expected = @" - ╒════╕ -╔╡1234╞═╗ -║╘════╛ ║ -╚═══════╝"; - break; - case 10: - Assert.Equal (new Rect (0, 0, 10, 4), win.Frame); - expected = @" - ╒════╕ -╔╡1234╞══╗ -║╘════╛ ║ -╚════════╝"; - break; - } - _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); - Application.End (rs); - } - - [Theory, AutoInitShutdown] - [InlineData (0)] - [InlineData (1)] - [InlineData (2)] - [InlineData (3)] - [InlineData (4)] - [InlineData (5)] - [InlineData (6)] - [InlineData (7)] - [InlineData (8)] - [InlineData (9)] - [InlineData (10)] - public void Border_With_Title_Border_Double_Thickness_Top_Four_Size_Width (int width) - { - var win = new Window () { - Title = "1234", - Width = Dim.Fill (), - Height = Dim.Fill (), - BorderStyle = LineStyle.Double, - }; - win.Border.Thickness.Top = 4; - - var rs = Application.Begin (win); - bool firstIteration = false; - - ((FakeDriver)Application.Driver).SetBufferSize (width, 5); - Application.RunIteration (ref rs, ref firstIteration); - var expected = string.Empty; - - switch (width) { - case 1: - Assert.Equal (new Rect (0, 0, 1, 5), win.Frame); - expected = @" -║ -║ -║"; - break; - case 2: - Assert.Equal (new Rect (0, 0, 2, 5), win.Frame); - expected = @" -╔╗ -║║ -╚╝"; - break; - case 3: - Assert.Equal (new Rect (0, 0, 3, 5), win.Frame); - expected = @" -╔═╗ -║ ║ -╚═╝"; - break; - case 4: - Assert.Equal (new Rect (0, 0, 4, 5), win.Frame); - expected = @" - ╒╕ -╔╡╞╗ -║╘╛║ -╚══╝"; - break; - case 5: - Assert.Equal (new Rect (0, 0, 5, 5), win.Frame); - expected = @" - ╒═╕ -╔╡1╞╗ -║╘═╛║ -╚═══╝"; - break; - case 6: - Assert.Equal (new Rect (0, 0, 6, 5), win.Frame); - expected = @" - ╒══╕ -╔╡12╞╗ -║╘══╛║ -╚════╝"; - break; - case 7: - Assert.Equal (new Rect (0, 0, 7, 5), win.Frame); - expected = @" - ╒═══╕ -╔╡123╞╗ -║╘═══╛║ -╚═════╝"; - break; - case 8: - Assert.Equal (new Rect (0, 0, 8, 5), win.Frame); - expected = @" - ╒════╕ -╔╡1234╞╗ -║╘════╛║ -╚══════╝"; - break; - case 9: - Assert.Equal (new Rect (0, 0, 9, 5), win.Frame); - expected = @" - ╒════╕ -╔╡1234╞═╗ -║╘════╛ ║ -╚═══════╝"; - break; - case 10: - Assert.Equal (new Rect (0, 0, 10, 5), win.Frame); - expected = @" - ╒════╕ -╔╡1234╞══╗ -║╘════╛ ║ -╚════════╝"; - break; - } - _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output); - Application.End (rs); - } } diff --git a/UnitTests/View/Text/AutoSizeTextTests.cs b/UnitTests/View/Text/AutoSizeTextTests.cs index f57df1765..95013036a 100644 --- a/UnitTests/View/Text/AutoSizeTextTests.cs +++ b/UnitTests/View/Text/AutoSizeTextTests.cs @@ -2513,7 +2513,7 @@ Y Application.End (rs); } - [Theory] [AutoInitShutdown] + [Theory, AutoInitShutdown] [InlineData (true)] [InlineData (false)] public void View_Draw_Horizontal_Simple_TextAlignments (bool autoSize) @@ -2568,7 +2568,7 @@ Y Assert.Equal (new Rect (0, 0, width + 2, 6), pos); } - [Theory] [AutoInitShutdown] + [Theory, AutoInitShutdown] [InlineData (true)] [InlineData (false)] public void View_Draw_Vertical_Simple_TextAlignments (bool autoSize) diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index 64820150a..f4d8282f1 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -862,7 +862,7 @@ public class ViewTests { view.Dispose (); } - [Theory] [AutoInitShutdown] + [Theory, AutoInitShutdown] [InlineData (true)] [InlineData (false)] public void Clear_Does_Not_Spillover_Its_Parent (bool label) @@ -903,11 +903,11 @@ cccccccccccccccccccc", output); Colors.Base.Focus }; if (label) { - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 111111111111111111110 111111111111111111110", Application.Driver, attributes); } else { - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 222222222222222222220 111111111111111111110", Application.Driver, attributes); } @@ -919,7 +919,7 @@ cccccccccccccccccccc", output); v.SetFocus (); Assert.True (v.HasFocus); Application.Refresh (); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 222222222222222222220 111111111111111111110", Application.Driver, attributes); } diff --git a/UnitTests/Views/AllViewsTests.cs b/UnitTests/Views/AllViewsTests.cs index 2b1229e6a..0e9a7da1b 100644 --- a/UnitTests/Views/AllViewsTests.cs +++ b/UnitTests/Views/AllViewsTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using Terminal.Gui.Views; using Xunit; using Xunit.Abstractions; diff --git a/UnitTests/Views/ComboBoxTests.cs b/UnitTests/Views/ComboBoxTests.cs index c1ab7ca9b..9cad4b2b3 100644 --- a/UnitTests/Views/ComboBoxTests.cs +++ b/UnitTests/Views/ComboBoxTests.cs @@ -839,7 +839,7 @@ Three ", output); cb.Subviews [1].GetNormalColor () }; - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 000000 222222 222222 @@ -851,7 +851,7 @@ Three ", output); Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("", cb.Text); cb.Draw (); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 000000 222222 000002 @@ -863,7 +863,7 @@ Three ", output); Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("", cb.Text); cb.Draw (); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 000000 222222 222222 @@ -881,7 +881,7 @@ Three ", output); Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); cb.Draw (); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 000000 222222 222222 @@ -893,7 +893,7 @@ Three ", output); Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); cb.Draw (); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 000000 222222 000002 @@ -905,7 +905,7 @@ Three ", output); Assert.Equal (2, cb.SelectedItem); Assert.Equal ("Three", cb.Text); cb.Draw (); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 000000 000002 222222 diff --git a/UnitTests/Views/DatePickerTests.cs b/UnitTests/Views/DatePickerTests.cs index 8685af417..4d39daf11 100644 --- a/UnitTests/Views/DatePickerTests.cs +++ b/UnitTests/Views/DatePickerTests.cs @@ -1,6 +1,6 @@ using System; using System.Globalization; -using Terminal.Gui.Views; +using Terminal.Gui; using Xunit; namespace Terminal.Gui.ViewsTests; diff --git a/UnitTests/Views/MenuBarTests.cs b/UnitTests/Views/MenuBarTests.cs index 7f6fe8316..adfc2094d 100644 --- a/UnitTests/Views/MenuBarTests.cs +++ b/UnitTests/Views/MenuBarTests.cs @@ -1539,7 +1539,7 @@ Edit menu.ColorScheme.Disabled }; - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 00000000000000", Application.Driver, attributes); Assert.True (menu.MouseEvent (new MouseEvent { @@ -1549,7 +1549,7 @@ Edit View = menu })); top.Draw (); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 11111100000000 00000000000000 01111111111110 @@ -1565,7 +1565,7 @@ Edit View = top.Subviews [1] })); top.Subviews [1].Draw (); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 11111100000000 00000000000000 01111111111110 @@ -1581,7 +1581,7 @@ Edit View = top.Subviews [1] })); top.Subviews [1].Draw (); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 11111100000000 00000000000000 01111111111110 diff --git a/UnitTests/Views/OverlappedTests.cs b/UnitTests/Views/OverlappedTests.cs index c2ecedbda..d02549e2f 100644 --- a/UnitTests/Views/OverlappedTests.cs +++ b/UnitTests/Views/OverlappedTests.cs @@ -708,7 +708,7 @@ public class OverlappedTests { // 1 Colors.Base.Normal }; - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 0000000000 0111110000 0111110000 @@ -732,7 +732,7 @@ public class OverlappedTests { │ │ │ │ └───┘", _output); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 0000000000 0000000000 0011111000 diff --git a/UnitTests/Views/RuneCellTests.cs b/UnitTests/Views/RuneCellTests.cs index 567c18142..1bb04ad67 100644 --- a/UnitTests/Views/RuneCellTests.cs +++ b/UnitTests/Views/RuneCellTests.cs @@ -119,12 +119,12 @@ Error "; 2222220000 3333000000 4444400000"; - TestHelpers.AssertDriverColorsAre (expectedColor, driver: Application.Driver, attributes); + TestHelpers.AssertDriverAttributesAre (expectedColor, driver: Application.Driver, attributes); tv.WordWrap = true; Application.Refresh (); TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); - TestHelpers.AssertDriverColorsAre (expectedColor, driver: Application.Driver, attributes); + TestHelpers.AssertDriverAttributesAre (expectedColor, driver: Application.Driver, attributes); tv.CursorPosition = new Point (6, 2); tv.SelectionStartColumn = 0; @@ -152,7 +152,7 @@ Dialogror "; 4444444444 4444000000 4444444440"; - TestHelpers.AssertDriverColorsAre (expectedColor, driver: Application.Driver, attributes); + TestHelpers.AssertDriverAttributesAre (expectedColor, driver: Application.Driver, attributes); tv.Undo (); tv.CursorPosition = new Point (0, 3); @@ -183,7 +183,7 @@ ror "; 4444000000 4444440000 4440000000"; - TestHelpers.AssertDriverColorsAre (expectedColor, driver: Application.Driver, attributes); + TestHelpers.AssertDriverAttributesAre (expectedColor, driver: Application.Driver, attributes); Application.End (rs); } diff --git a/UnitTests/Views/ScrollViewTests.cs b/UnitTests/Views/ScrollViewTests.cs index 0b01b565e..f84d1d05f 100644 --- a/UnitTests/Views/ScrollViewTests.cs +++ b/UnitTests/Views/ScrollViewTests.cs @@ -445,7 +445,7 @@ namespace Terminal.Gui.ViewsTests { public override bool OnEnter (View view) { - Border.BorderStyle = LineStyle.None; + Border.LineStyle = LineStyle.None; Border.Thickness = new Thickness (0); labelFill.Visible = true; view = this; @@ -454,7 +454,7 @@ namespace Terminal.Gui.ViewsTests { public override bool OnLeave (View view) { - Border.BorderStyle = LineStyle.Single; + Border.LineStyle = LineStyle.Single; Border.Thickness = new Thickness (1); labelFill.Visible = false; if (view == null) @@ -503,7 +503,7 @@ namespace Terminal.Gui.ViewsTests { Colors.Base.Normal }; - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 00000000000000000000000 00000000000000000000000 00000000000000000000000 @@ -542,7 +542,7 @@ namespace Terminal.Gui.ViewsTests { At 15,15", output); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 00000000000000000000000 00000000000000000000000 00000000000000000000000 @@ -580,7 +580,7 @@ namespace Terminal.Gui.ViewsTests { At 15,15", output); - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 00000000000000000000000 00000000000000000000000 00000000000000000000000 diff --git a/UnitTests/Views/TableViewTests.cs b/UnitTests/Views/TableViewTests.cs index e267e18c3..35daeb709 100644 --- a/UnitTests/Views/TableViewTests.cs +++ b/UnitTests/Views/TableViewTests.cs @@ -836,7 +836,7 @@ namespace Terminal.Gui.ViewsTests { 01000 "; - TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { + TestHelpers.AssertDriverAttributesAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -883,7 +883,7 @@ namespace Terminal.Gui.ViewsTests { var invertFocus = new Attribute (tv.ColorScheme.Focus.Background, tv.ColorScheme.Focus.Foreground); var invertHotNormal = new Attribute (tv.ColorScheme.HotNormal.Background, tv.ColorScheme.HotNormal.Foreground); - TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { + TestHelpers.AssertDriverAttributesAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -938,7 +938,7 @@ namespace Terminal.Gui.ViewsTests { 21222 "; - TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { + TestHelpers.AssertDriverAttributesAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -971,7 +971,7 @@ namespace Terminal.Gui.ViewsTests { // now we only see 2 colors used (the selected cell color and Normal // rowHighlight should no longer be used because the delegate returned null // (now that the cell value is 5 - which does not match the conditional) - TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { + TestHelpers.AssertDriverAttributesAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -1030,7 +1030,7 @@ namespace Terminal.Gui.ViewsTests { 01020 "; - TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { + TestHelpers.AssertDriverAttributesAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -1063,7 +1063,7 @@ namespace Terminal.Gui.ViewsTests { // now we only see 2 colors used (the selected cell color and Normal // cellHighlight should no longer be used because the delegate returned null // (now that the cell value is 5 - which does not match the conditional) - TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { + TestHelpers.AssertDriverAttributesAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -2112,7 +2112,7 @@ namespace Terminal.Gui.ViewsTests { 00000000000000000000 01111101101111111110 "; - TestHelpers.AssertDriverColorsAre (expected, driver: Application.Driver, new Attribute [] { tv.ColorScheme.Normal, color }); + TestHelpers.AssertDriverAttributesAre (expected, driver: Application.Driver, new Attribute [] { tv.ColorScheme.Normal, color }); } @@ -2232,7 +2232,7 @@ namespace Terminal.Gui.ViewsTests { 0111110 0000000"; - TestHelpers.AssertDriverColorsAre (expected, driver: Application.Driver, normal, focus); + TestHelpers.AssertDriverAttributesAre (expected, driver: Application.Driver, normal, focus); } [Fact, AutoInitShutdown] @@ -2289,7 +2289,7 @@ namespace Terminal.Gui.ViewsTests { 0101010 0000000"; - TestHelpers.AssertDriverColorsAre (expected, driver: Application.Driver, normal, focus); + TestHelpers.AssertDriverAttributesAre (expected, driver: Application.Driver, normal, focus); } [Fact, AutoInitShutdown] @@ -2784,7 +2784,7 @@ A B C 000000 111111"; - TestHelpers.AssertDriverColorsAre (expected, driver: Application.Driver, normal, focus); + TestHelpers.AssertDriverAttributesAre (expected, driver: Application.Driver, normal, focus); } public static DataTableSource BuildTable (int cols, int rows) diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index d47ba2bfe..d12fac58b 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -59,14 +59,14 @@ public class TextFieldTests { }; // TAB to jump between text fields. - TestHelpers.AssertDriverColorsAre ("0000000", driver: Application.Driver, attributes); + TestHelpers.AssertDriverAttributesAre ("0000000", driver: Application.Driver, attributes); _textField.NewKeyDownEvent (new (KeyCode.CursorRight | KeyCode.CtrlMask | KeyCode.ShiftMask)); bool first = true; Application.RunIteration (ref rs, ref first); Assert.Equal (4, _textField.CursorPosition); // TAB to jump between text fields. - TestHelpers.AssertDriverColorsAre ("1111000", driver: Application.Driver, attributes); + TestHelpers.AssertDriverAttributesAre ("1111000", driver: Application.Driver, attributes); } [Fact] diff --git a/UnitTests/Views/TextViewTests.cs b/UnitTests/Views/TextViewTests.cs index 01138bbc9..58546ba93 100644 --- a/UnitTests/Views/TextViewTests.cs +++ b/UnitTests/Views/TextViewTests.cs @@ -293,7 +293,7 @@ public class TextViewTests { }; // TAB to jump between text fields. - TestHelpers.AssertDriverColorsAre ("0000000", driver: Application.Driver, attributes); + TestHelpers.AssertDriverAttributesAre ("0000000", driver: Application.Driver, attributes); _textView.NewKeyDownEvent (new (KeyCode.CursorRight | KeyCode.CtrlMask | KeyCode.ShiftMask)); @@ -301,7 +301,7 @@ public class TextViewTests { Application.RunIteration (ref rs, ref first); Assert.Equal (new Point (4, 0), _textView.CursorPosition); // TAB to jump between text fields. - TestHelpers.AssertDriverColorsAre ("1111000", driver: Application.Driver, attributes); + TestHelpers.AssertDriverAttributesAre ("1111000", driver: Application.Driver, attributes); } [Fact] diff --git a/UnitTests/Views/TreeViewTests.cs b/UnitTests/Views/TreeViewTests.cs index bfce38cf6..6b1ab148b 100644 --- a/UnitTests/Views/TreeViewTests.cs +++ b/UnitTests/Views/TreeViewTests.cs @@ -840,7 +840,7 @@ public class TreeViewTests { └─pink ", _output); // Should all be the same color - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 0000000000 0000000000 0000000000 @@ -869,7 +869,7 @@ public class TreeViewTests { ", _output); // but now the item (only not lines) appear // in pink when they are the word "pink" - TestHelpers.AssertDriverColorsAre (@" + TestHelpers.AssertDriverAttributesAre (@" 00000000 00001111 0000000000