using NStack; using System; namespace Terminal.Gui { /// /// Specifies the border style for a and to be used by the class. /// public enum BorderStyle { /// /// No border is drawn. /// None, /// /// The border is drawn with a single line limits. /// Single, /// /// The border is drawn with a double line limits. /// Double, /// /// The border is drawn with a single line and rounded corners limits. /// Rounded } /// /// Describes the thickness of a frame around a rectangle. Four values describe /// the , , , and sides /// of the rectangle, respectively. /// public struct Thickness { /// /// Gets or sets the width, in integers, of the left side of the bounding rectangle. /// public int Left; /// /// Gets or sets the width, in integers, of the upper side of the bounding rectangle. /// public int Top; /// /// Gets or sets the width, in integers, of the right side of the bounding rectangle. /// public int Right; /// /// Gets or sets the width, in integers, of the lower side of the bounding rectangle. /// public int Bottom; /// /// Initializes a new instance of the structure that has the /// specified uniform length on each side. /// /// public Thickness (int length) { if (length < 0) { throw new ArgumentException ("Invalid value for this property."); } Left = Top = Right = Bottom = length; } /// /// Initializes a new instance of the structure that has specific /// lengths (supplied as a ) applied to each side of the rectangle. /// /// /// /// /// public Thickness (int left, int top, int right, int bottom) { if (left < 0 || top < 0 || right < 0 || bottom < 0) { throw new ArgumentException ("Invalid value for this property."); } Left = left; Top = top; Right = right; Bottom = bottom; } /// Returns the fully qualified type name of this instance. /// The fully qualified type name. public override string ToString () { return $"(Left={Left},Top={Top},Right={Right},Bottom={Bottom})"; } } /// /// Draws a border, background, or both around another element. /// public class Border { private int marginFrame => DrawMarginFrame ? 1 : 0; /// /// A sealed derived class to implement feature. /// This is only a wrapper to get borders on a toplevel and is recommended using another /// derived, like where is possible to have borders with or without /// border line or spacing around. /// public sealed class ToplevelContainer : Toplevel { /// public override Border Border { get => base.Border; set { if (base.Border != null && base.Border.Child != null && value.Child == null) { value.Child = base.Border.Child; } base.Border = value; if (value == null) { return; } Rect frame; if (Border.Child != null && (Border.Child.Width is Dim || Border.Child.Height is Dim)) { frame = Rect.Empty; } else { frame = Frame; } AdjustContentView (frame); Border.BorderChanged += Border_BorderChanged; } } void Border_BorderChanged (Border border) { Rect frame; if (Border.Child != null && (Border.Child.Width is Dim || Border.Child.Height is Dim)) { frame = Rect.Empty; } else { frame = Frame; } AdjustContentView (frame); } /// /// Initializes with default null values. /// public ToplevelContainer () : this (null, string.Empty) { } /// /// Initializes a with a /// /// The border. /// The title. public ToplevelContainer (Border border, string title = null) { Initialize (Rect.Empty, border, title ?? string.Empty); } /// /// Initializes a with a /// /// The frame. /// The border. /// The title. public ToplevelContainer (Rect frame, Border border, string title = null) : base (frame) { Initialize (frame, border, title ?? string.Empty); } private void Initialize (Rect frame, Border border, string title) { ColorScheme = Colors.TopLevel; if (border == null) { Border = new Border () { BorderStyle = BorderStyle.Single, Title = (ustring)title }; } else { Border = border; } AdjustContentView (frame); } void AdjustContentView (Rect frame) { var borderLength = Border.DrawMarginFrame ? 1 : 0; var sumPadding = Border.GetSumThickness (); var wp = new Point (); var wb = new Size (); if (frame == Rect.Empty) { wp.X = borderLength + sumPadding.Left; wp.Y = borderLength + sumPadding.Top; wb.Width = borderLength + sumPadding.Right; wb.Height = borderLength + sumPadding.Bottom; if (Border.Child == null) { Border.Child = new ChildContentView (this) { X = wp.X, Y = wp.Y, Width = Dim.Fill (wb.Width), Height = Dim.Fill (wb.Height) }; } else { Border.Child.X = wp.X; Border.Child.Y = wp.Y; Border.Child.Width = Dim.Fill (wb.Width); Border.Child.Height = Dim.Fill (wb.Height); } } else { wb.Width = (2 * borderLength) + sumPadding.Right + sumPadding.Left; wb.Height = (2 * borderLength) + sumPadding.Bottom + sumPadding.Top; var cFrame = new Rect (borderLength + sumPadding.Left, borderLength + sumPadding.Top, frame.Width - wb.Width, frame.Height - wb.Height); if (Border.Child == null) { Border.Child = new ChildContentView (cFrame, this); } else { Border.Child.Frame = cFrame; } } if (Subviews?.Count == 0) base.Add (Border.Child); Border.ChildContainer = this; } /// public override void Add (View view) { Border.Child.Add (view); if (view.CanFocus) { CanFocus = true; } AddMenuStatusBar (view); } /// public override void Remove (View view) { if (view == null) { return; } SetNeedsDisplay (); var touched = view.Frame; Border.Child.Remove (view); if (Border.Child.InternalSubviews.Count < 1) { CanFocus = false; } RemoveMenuStatusBar (view); } /// public override void RemoveAll () { Border.Child.RemoveAll (); } /// public override void Redraw (Rect bounds) { if (!NeedDisplay.IsEmpty) { Driver.SetAttribute (GetNormalColor ()); Clear (); } var savedClip = Border.Child.ClipToBounds (); Border.Child.Redraw (Border.Child.Bounds); Driver.Clip = savedClip; ClearLayoutNeeded (); ClearNeedsDisplay (); Driver.SetAttribute (GetNormalColor ()); Border.DrawContent (this, false); if (HasFocus) Driver.SetAttribute (ColorScheme.HotNormal); if (Border.DrawMarginFrame) { if (!ustring.IsNullOrEmpty (Border.Title)) Border.DrawTitle (this); else Border.DrawTitle (this, Frame); } Driver.SetAttribute (GetNormalColor ()); // Checks if there are any SuperView view which intersect with this window. if (SuperView != null) { SuperView.SetNeedsLayout (); SuperView.SetNeedsDisplay (); } } /// public override void OnCanFocusChanged () { if (Border.Child != null) { Border.Child.CanFocus = CanFocus; } base.OnCanFocusChanged (); } } private class ChildContentView : View { View instance; public ChildContentView (Rect frame, View instance) : base (frame) { this.instance = instance; } public ChildContentView (View instance) { this.instance = instance; } public override bool MouseEvent (MouseEvent mouseEvent) { return instance.MouseEvent (mouseEvent); } } /// /// Invoked when any property of Border changes (except ). /// public event Action BorderChanged; private BorderStyle borderStyle; private bool drawMarginFrame; private Thickness borderThickness; private Color? borderBrush; private Color? background; private Thickness padding; private bool effect3D; private Point effect3DOffset = new Point (1, 1); private Attribute? effect3DBrush; private ustring title = ustring.Empty; private View child; /// /// Specifies the for a view. /// public BorderStyle BorderStyle { get => borderStyle; set { if (value != BorderStyle.None && !drawMarginFrame) { // Ensures drawn the border lines. drawMarginFrame = true; } borderStyle = value; OnBorderChanged (); } } /// /// Gets or sets if a margin frame is drawn around the regardless the /// public bool DrawMarginFrame { get => drawMarginFrame; set { if (borderStyle != BorderStyle.None && (!value || !drawMarginFrame)) { // Ensures drawn the border lines. drawMarginFrame = true; } else { drawMarginFrame = value; } OnBorderChanged (); } } /// /// Gets or sets the relative of a . /// public Thickness BorderThickness { get => borderThickness; set { borderThickness = value; OnBorderChanged (); } } /// /// Gets or sets the that draws the outer border color. /// public Color BorderBrush { get => borderBrush != null ? (Color)borderBrush : (Color)(-1); set { borderBrush = value; OnBorderChanged (); } } /// /// Gets or sets the that fills the area between the bounds of a . /// public Color Background { get => background != null ? (Color)background : (Color)(-1); set { background = value; OnBorderChanged (); } } /// /// Gets or sets a value that describes the amount of space between a /// and its child element. /// public Thickness Padding { get => padding; set { padding = value; OnBorderChanged (); } } /// /// Gets the rendered width of this element. /// public int ActualWidth { get { var driver = Application.Driver; if (Parent?.Border == null) { return Math.Min (Child?.Frame.Width + (2 * marginFrame) + Padding.Right + BorderThickness.Right + Padding.Left + BorderThickness.Left ?? 0, driver.Cols); } return Math.Min (Parent.Frame.Width, driver.Cols); } } /// /// Gets the rendered height of this element. /// public int ActualHeight { get { var driver = Application.Driver; if (Parent?.Border == null) { return Math.Min (Child?.Frame.Height + (2 * marginFrame) + Padding.Bottom + BorderThickness.Bottom + Padding.Top + BorderThickness.Top ?? 0, driver.Rows); } return Math.Min (Parent.Frame.Height, driver.Rows); } } /// /// Gets or sets the single child element of a . /// public View Child { get => child; set { child = value; if (child != null && Parent != null) { Parent.Removed += Parent_Removed; } } } private void Parent_Removed (View obj) { if (borderBrush != null) { BorderBrush = default; } if (background != null) { Background = default; } child.Removed -= Parent_Removed; } /// /// Gets the parent parent if any. /// public View Parent { get => Child?.SuperView; } /// /// Gets or private sets by the /// public ToplevelContainer ChildContainer { get; private set; } /// /// Gets or sets the 3D effect around the . /// public bool Effect3D { get => effect3D; set { effect3D = value; OnBorderChanged (); } } /// /// Get or sets the offset start position for the /// public Point Effect3DOffset { get => effect3DOffset; set { effect3DOffset = value; OnBorderChanged (); } } /// /// Gets or sets the color for the /// public Attribute? Effect3DBrush { get => effect3DBrush; set { effect3DBrush = value; OnBorderChanged (); } } /// /// The title to be displayed for this view. /// public ustring Title { get => title; set { title = value; OnBorderChanged (); } } /// /// Calculate the sum of the and the /// /// The total of the public Thickness GetSumThickness () { return new Thickness () { Left = Padding.Left + BorderThickness.Left, Top = Padding.Top + BorderThickness.Top, Right = Padding.Right + BorderThickness.Right, Bottom = Padding.Bottom + BorderThickness.Bottom }; } /// /// Drawn the more the /// more the and the . /// /// The view to draw. /// If it will clear or not the content area. public void DrawContent (View view = null, bool fill = true) { if (Child == null) { Child = view; } if (Parent?.Border != null) { DrawParentBorder (Parent.ViewToScreen (Parent.Bounds), fill); } else { DrawChildBorder (Child.ViewToScreen (Child.Bounds), fill); } } /// /// Same as but drawing full frames for all borders. /// public void DrawFullContent () { var borderThickness = BorderThickness; var padding = Padding; var marginFrame = DrawMarginFrame ? 1 : 0; var driver = Application.Driver; Rect scrRect; if (Parent?.Border != null) { scrRect = Parent.ViewToScreen (Parent.Bounds); } else { scrRect = Child.ViewToScreen (Child.Bounds); } Rect borderRect; if (Parent?.Border != null) { borderRect = scrRect; } else { borderRect = new Rect () { X = scrRect.X - marginFrame - padding.Left - borderThickness.Left, Y = scrRect.Y - marginFrame - padding.Top - borderThickness.Top, Width = ActualWidth, Height = ActualHeight }; } var savedAttribute = driver.GetAttribute (); // Draw 3D effects if (Effect3D) { driver.SetAttribute (GetEffect3DBrush ()); var effectBorder = new Rect () { X = borderRect.X + Effect3DOffset.X, Y = borderRect.Y + Effect3DOffset.Y, Width = ActualWidth, Height = ActualHeight }; //Child.Clear (effectBorder); for (int r = effectBorder.Y; r < Math.Min (effectBorder.Bottom, driver.Rows); r++) { for (int c = effectBorder.X; c < Math.Min (effectBorder.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } } } // Draw border thickness SetBorderBrush (driver); Child.Clear (borderRect); borderRect = new Rect () { X = borderRect.X + borderThickness.Left, Y = borderRect.Y + borderThickness.Top, Width = Math.Max (borderRect.Width - borderThickness.Right - borderThickness.Left, 0), Height = Math.Max (borderRect.Height - borderThickness.Bottom - borderThickness.Top, 0) }; if (borderRect != scrRect) { // Draw padding driver.SetAttribute (new Attribute (Background)); Child.Clear (borderRect); } SetBorderBrushBackground (driver); // Draw margin frame if (DrawMarginFrame) { if (Parent?.Border != null) { var sumPadding = GetSumThickness (); borderRect = new Rect () { X = scrRect.X + sumPadding.Left, Y = scrRect.Y + sumPadding.Top, Width = Math.Max (scrRect.Width - sumPadding.Right - sumPadding.Left, 0), Height = Math.Max (scrRect.Height - sumPadding.Bottom - sumPadding.Top, 0) }; } else { borderRect = new Rect () { X = borderRect.X + padding.Left, Y = borderRect.Y + padding.Top, Width = Math.Max (borderRect.Width - padding.Right - padding.Left, 0), Height = Math.Max (borderRect.Height - padding.Bottom - padding.Top, 0) }; } if (borderRect.Width > 0 && borderRect.Height > 0) { driver.DrawWindowFrame (borderRect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill: true, this); } } driver.SetAttribute (savedAttribute); } private void DrawChildBorder (Rect frame, bool fill = true) { var drawMarginFrame = DrawMarginFrame ? 1 : 0; var sumThickness = GetSumThickness (); var padding = Padding; var effect3DOffset = Effect3DOffset; var driver = Application.Driver; var savedAttribute = driver.GetAttribute (); SetBorderBrush (driver); // Draw the upper BorderThickness for (int r = frame.Y - drawMarginFrame - sumThickness.Top; r < frame.Y - drawMarginFrame - padding.Top; r++) { for (int c = frame.X - drawMarginFrame - sumThickness.Left; c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the left BorderThickness for (int r = frame.Y - drawMarginFrame - padding.Top; r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) { for (int c = frame.X - drawMarginFrame - sumThickness.Left; c < frame.X - drawMarginFrame - padding.Left; c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the right BorderThickness for (int r = frame.Y - drawMarginFrame - padding.Top; r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) { for (int c = frame.Right + drawMarginFrame + padding.Right; c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the lower BorderThickness for (int r = frame.Bottom + drawMarginFrame + padding.Bottom; r < Math.Min (frame.Bottom + drawMarginFrame + sumThickness.Bottom, driver.Rows); r++) { for (int c = frame.X - drawMarginFrame - sumThickness.Left; c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } SetBackground (driver); // Draw the upper Padding for (int r = frame.Y - drawMarginFrame - padding.Top; r < frame.Y - drawMarginFrame; r++) { for (int c = frame.X - drawMarginFrame - padding.Left; c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the left Padding for (int r = frame.Y - drawMarginFrame; r < Math.Min (frame.Bottom + drawMarginFrame, driver.Rows); r++) { for (int c = frame.X - drawMarginFrame - padding.Left; c < frame.X - drawMarginFrame; c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the right Padding for (int r = frame.Y - drawMarginFrame; r < Math.Min (frame.Bottom + drawMarginFrame, driver.Rows); r++) { for (int c = frame.Right + drawMarginFrame; c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the lower Padding for (int r = frame.Bottom + drawMarginFrame; r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) { for (int c = frame.X - drawMarginFrame - padding.Left; c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } SetBorderBrushBackground (driver); // Draw the MarginFrame if (DrawMarginFrame) { var rect = new Rect () { X = frame.X - drawMarginFrame, Y = frame.Y - drawMarginFrame, Width = frame.Width + (2 * drawMarginFrame), Height = frame.Height + (2 * drawMarginFrame) }; if (rect.Width > 0 && rect.Height > 0) { driver.DrawWindowFrame (rect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill, this); DrawTitle (Child); } } if (Effect3D) { driver.SetAttribute (GetEffect3DBrush ()); // Draw the upper Effect3D for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y; r >= 0 && r < frame.Y - drawMarginFrame - sumThickness.Top; r++) { for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X; c >= 0 && c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X, driver.Cols); c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } } // Draw the left Effect3D for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y; r >= 0 && r < Math.Min (frame.Bottom + drawMarginFrame + sumThickness.Bottom + effect3DOffset.Y, driver.Rows); r++) { for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X; c >= 0 && c < frame.X - drawMarginFrame - sumThickness.Left; c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } } // Draw the right Effect3D for (int r = frame.Y - drawMarginFrame - sumThickness.Top + effect3DOffset.Y; r >= 0 && r < Math.Min (frame.Bottom + drawMarginFrame + sumThickness.Bottom + effect3DOffset.Y, driver.Rows); r++) { for (int c = frame.Right + drawMarginFrame + sumThickness.Right; c >= 0 && c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X, driver.Cols); c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } } // Draw the lower Effect3D for (int r = frame.Bottom + drawMarginFrame + sumThickness.Bottom; r >= 0 && r < Math.Min (frame.Bottom + drawMarginFrame + sumThickness.Bottom + effect3DOffset.Y, driver.Rows); r++) { for (int c = frame.X - drawMarginFrame - sumThickness.Left + effect3DOffset.X; c >= 0 && c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right + effect3DOffset.X, driver.Cols); c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } } } driver.SetAttribute (savedAttribute); } private void DrawParentBorder (Rect frame, bool fill = true) { var sumThickness = GetSumThickness (); var borderThickness = BorderThickness; var effect3DOffset = Effect3DOffset; var driver = Application.Driver; var savedAttribute = driver.GetAttribute (); SetBorderBrush (driver); // Draw the upper BorderThickness for (int r = frame.Y; r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) { for (int c = frame.X; c < Math.Min (frame.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the left BorderThickness for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) { for (int c = frame.X; c < Math.Min (frame.X + borderThickness.Left, frame.Right); c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the right BorderThickness for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) { for (int c = Math.Max (frame.Right - borderThickness.Right, frame.X); c < Math.Min (frame.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the lower BorderThickness for (int r = Math.Max (frame.Bottom - borderThickness.Bottom, frame.Y); r < Math.Min (frame.Bottom, driver.Rows); r++) { for (int c = frame.X; c < Math.Min (frame.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } SetBackground (driver); // Draw the upper Padding for (int r = frame.Y + borderThickness.Top; r < Math.Min (frame.Y + sumThickness.Top, frame.Bottom - borderThickness.Bottom); r++) { for (int c = frame.X + borderThickness.Left; c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the left Padding for (int r = frame.Y + sumThickness.Top; r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) { for (int c = frame.X + borderThickness.Left; c < Math.Min (frame.X + sumThickness.Left, frame.Right - borderThickness.Right); c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the right Padding for (int r = frame.Y + sumThickness.Top; r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) { for (int c = Math.Max (frame.Right - sumThickness.Right, frame.X + sumThickness.Left); c < Math.Max (frame.Right - borderThickness.Right, frame.X + sumThickness.Left); c++) { AddRuneAt (driver, c, r, ' '); } } // Draw the lower Padding for (int r = Math.Max (frame.Bottom - sumThickness.Bottom, frame.Y + borderThickness.Top); r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) { for (int c = frame.X + borderThickness.Left; c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } } SetBorderBrushBackground (driver); // Draw the MarginFrame if (DrawMarginFrame) { var rect = new Rect () { X = frame.X + sumThickness.Left, Y = frame.Y + sumThickness.Top, Width = Math.Max (frame.Width - sumThickness.Right - sumThickness.Left, 0), Height = Math.Max (frame.Height - sumThickness.Bottom - sumThickness.Top, 0) }; if (rect.Width > 0 && rect.Height > 0) { driver.DrawWindowFrame (rect, 1, 1, 1, 1, BorderStyle != BorderStyle.None, fill, this); DrawTitle (Parent); } } if (Effect3D) { driver.SetAttribute (GetEffect3DBrush ()); // Draw the upper Effect3D for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0); r < frame.Y; r++) { for (int c = Math.Max (frame.X + effect3DOffset.X, 0); c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } } // Draw the left Effect3D for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0); r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) { for (int c = Math.Max (frame.X + effect3DOffset.X, 0); c < frame.X; c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } } // Draw the right Effect3D for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0); r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) { for (int c = frame.Right; c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } } // Draw the lower Effect3D for (int r = frame.Bottom; r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) { for (int c = Math.Max (frame.X + effect3DOffset.X, 0); c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } } } driver.SetAttribute (savedAttribute); } private void SetBorderBrushBackground (ConsoleDriver driver) { if (borderBrush != null && background != null) { driver.SetAttribute (new Attribute (BorderBrush, Background)); } else if (borderBrush != null && background == null) { driver.SetAttribute (new Attribute (BorderBrush, Parent.ColorScheme.Normal.Background)); } else if (borderBrush == null && background != null) { driver.SetAttribute (new Attribute (Parent.ColorScheme.Normal.Foreground, Background)); } else { driver.SetAttribute (Parent.ColorScheme.Normal); } } private void SetBackground (ConsoleDriver driver) { if (background != null) { driver.SetAttribute (new Attribute (Background)); } else { driver.SetAttribute (new Attribute (Parent.ColorScheme.Normal.Background)); } } private void SetBorderBrush (ConsoleDriver driver) { if (borderBrush != null) { driver.SetAttribute (new Attribute (BorderBrush)); } else { driver.SetAttribute (new Attribute (Parent.ColorScheme.Normal.Foreground)); } } private Attribute GetEffect3DBrush () { return Effect3DBrush == null ? new Attribute (Color.Gray, Color.DarkGray) : (Attribute)Effect3DBrush; } private void AddRuneAt (ConsoleDriver driver, int col, int row, Rune ch) { if (col < driver.Cols && row < driver.Rows && col > 0 && driver.Contents [row, col, 2] == 0 && Rune.ColumnWidth ((char)driver.Contents [row, col - 1, 0]) > 1) { driver.Contents [row, col, 1] = driver.GetAttribute (); return; } driver.Move (col, row); driver.AddRune (ch); } /// /// Draws the view to the screen. /// /// The view. public void DrawTitle (View view) { var driver = Application.Driver; if (DrawMarginFrame) { SetBorderBrushBackground (driver); SetHotNormalBackground (view, driver); var padding = view.Border.GetSumThickness (); Rect scrRect; if (view == Child) { scrRect = view.ViewToScreen (new Rect (0, 0, view.Frame.Width + 2, view.Frame.Height + 2)); scrRect = new Rect (scrRect.X - 1, scrRect.Y - 1, scrRect.Width, scrRect.Height); driver.DrawWindowTitle (scrRect, Title, 0, 0, 0, 0); } else { scrRect = view.ViewToScreen (new Rect (0, 0, view.Frame.Width, view.Frame.Height)); driver.DrawWindowTitle (scrRect, Parent.Border.Title, padding.Left, padding.Top, padding.Right, padding.Bottom); } } driver.SetAttribute (Child.GetNormalColor ()); } private void SetHotNormalBackground (View view, ConsoleDriver driver) { if (view.HasFocus) { if (background != null) { driver.SetAttribute (new Attribute (Child.ColorScheme.HotNormal.Foreground, Background)); } else { driver.SetAttribute (Child.ColorScheme.HotNormal); } } } /// /// Draws the to the screen. /// /// The view. /// The frame. public void DrawTitle (View view, Rect rect) { var driver = Application.Driver; if (DrawMarginFrame) { SetBorderBrushBackground (driver); SetHotNormalBackground (view, driver); var padding = Parent.Border.GetSumThickness (); var scrRect = Parent.ViewToScreen (new Rect (0, 0, rect.Width, rect.Height)); driver.DrawWindowTitle (scrRect, view.Text, padding.Left, padding.Top, padding.Right, padding.Bottom); } driver.SetAttribute (view.GetNormalColor ()); } /// /// Invoke the event. /// public virtual void OnBorderChanged () { BorderChanged?.Invoke (this); } } }