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);
}
}
}