12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061 |
- using NStack;
- using System;
- namespace Terminal.Gui {
- /// <summary>
- /// Specifies the border style for a <see cref="View"/> and to be used by the <see cref="Border"/> class.
- /// </summary>
- public enum BorderStyle {
- /// <summary>
- /// No border is drawn.
- /// </summary>
- None,
- /// <summary>
- /// The border is drawn with a single line limits.
- /// </summary>
- Single,
- /// <summary>
- /// The border is drawn with a double line limits.
- /// </summary>
- Double,
- /// <summary>
- /// The border is drawn with a single line and rounded corners limits.
- /// </summary>
- Rounded
- }
- /// <summary>
- /// Describes the thickness of a frame around a rectangle. Four <see cref="int"/> values describe
- /// the <see cref="Left"/>, <see cref="Top"/>, <see cref="Right"/>, and <see cref="Bottom"/> sides
- /// of the rectangle, respectively.
- /// </summary>
- public struct Thickness {
- /// <summary>
- /// Gets or sets the width, in integers, of the left side of the bounding rectangle.
- /// </summary>
- public int Left;
- /// <summary>
- /// Gets or sets the width, in integers, of the upper side of the bounding rectangle.
- /// </summary>
- public int Top;
- /// <summary>
- /// Gets or sets the width, in integers, of the right side of the bounding rectangle.
- /// </summary>
- public int Right;
- /// <summary>
- /// Gets or sets the width, in integers, of the lower side of the bounding rectangle.
- /// </summary>
- public int Bottom;
- /// <summary>
- /// Initializes a new instance of the <see cref="Thickness"/> structure that has the
- /// specified uniform length on each side.
- /// </summary>
- /// <param name="length"></param>
- public Thickness (int length)
- {
- if (length < 0) {
- throw new ArgumentException ("Invalid value for this property.");
- }
- Left = Top = Right = Bottom = length;
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="Thickness"/> structure that has specific
- /// lengths (supplied as a <see cref="int"/>) applied to each side of the rectangle.
- /// </summary>
- /// <param name="left"></param>
- /// <param name="top"></param>
- /// <param name="right"></param>
- /// <param name="bottom"></param>
- 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;
- }
- /// <summary>Returns the fully qualified type name of this instance.</summary>
- /// <returns>The fully qualified type name.</returns>
- public override string ToString ()
- {
- return $"(Left={Left},Top={Top},Right={Right},Bottom={Bottom})";
- }
- }
- /// <summary>
- /// Draws a border, background, or both around another element.
- /// </summary>
- public class Border {
- private int marginFrame => DrawMarginFrame ? 1 : 0;
- /// <summary>
- /// A sealed <see cref="Toplevel"/> derived class to implement <see cref="Border"/> feature.
- /// This is only a wrapper to get borders on a toplevel and is recommended using another
- /// derived, like <see cref="Window"/> where is possible to have borders with or without
- /// border line or spacing around.
- /// </summary>
- public sealed class ToplevelContainer : Toplevel {
- /// <inheritdoc/>
- 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);
- }
- /// <summary>
- /// Initializes with default null values.
- /// </summary>
- public ToplevelContainer () : this (null, string.Empty) { }
- /// <summary>
- /// Initializes a <see cref="ToplevelContainer"/> with a <see cref="LayoutStyle.Computed"/>
- /// </summary>
- /// <param name="border">The border.</param>
- /// <param name="title">The title.</param>
- public ToplevelContainer (Border border, string title = null)
- {
- Initialize (Rect.Empty, border, title ?? string.Empty);
- }
- /// <summary>
- /// Initializes a <see cref="ToplevelContainer"/> with a <see cref="LayoutStyle.Absolute"/>
- /// </summary>
- /// <param name="frame">The frame.</param>
- /// <param name="border">The border.</param>
- /// <param name="title">The title.</param>
- 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;
- }
- /// <inheritdoc/>
- public override void Add (View view)
- {
- Border.Child.Add (view);
- if (view.CanFocus) {
- CanFocus = true;
- }
- AddMenuStatusBar (view);
- }
- /// <inheritdoc/>
- 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);
- }
- /// <inheritdoc/>
- public override void RemoveAll ()
- {
- Border.Child.RemoveAll ();
- }
- /// <inheritdoc/>
- 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 ();
- }
- }
- /// <inheritdoc/>
- 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);
- }
- }
- /// <summary>
- /// Invoked when any property of Border changes (except <see cref="Child"/>).
- /// </summary>
- public event Action<Border> 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;
- /// <summary>
- /// Specifies the <see cref="Gui.BorderStyle"/> for a view.
- /// </summary>
- public BorderStyle BorderStyle {
- get => borderStyle;
- set {
- if (value != BorderStyle.None && !drawMarginFrame) {
- // Ensures drawn the border lines.
- drawMarginFrame = true;
- }
- borderStyle = value;
- OnBorderChanged ();
- }
- }
- /// <summary>
- /// Gets or sets if a margin frame is drawn around the <see cref="Child"/> regardless the <see cref="BorderStyle"/>
- /// </summary>
- public bool DrawMarginFrame {
- get => drawMarginFrame;
- set {
- if (borderStyle != BorderStyle.None
- && (!value || !drawMarginFrame)) {
- // Ensures drawn the border lines.
- drawMarginFrame = true;
- } else {
- drawMarginFrame = value;
- }
- OnBorderChanged ();
- }
- }
- /// <summary>
- /// Gets or sets the relative <see cref="Thickness"/> of a <see cref="Border"/>.
- /// </summary>
- public Thickness BorderThickness {
- get => borderThickness;
- set {
- borderThickness = value;
- OnBorderChanged ();
- }
- }
- /// <summary>
- /// Gets or sets the <see cref="Color"/> that draws the outer border color.
- /// </summary>
- public Color BorderBrush {
- get => borderBrush != null ? (Color)borderBrush : (Color)(-1);
- set {
- if (Enum.IsDefined (typeof (Color), value)) {
- borderBrush = value;
- OnBorderChanged ();
- }
- }
- }
- /// <summary>
- /// Gets or sets the <see cref="Color"/> that fills the area between the bounds of a <see cref="Border"/>.
- /// </summary>
- public Color Background {
- get => background != null ? (Color)background : (Color)(-1);
- set {
- if (Enum.IsDefined (typeof (Color), value)) {
- background = value;
- OnBorderChanged ();
- }
- }
- }
- /// <summary>
- /// Gets or sets a <see cref="Thickness"/> value that describes the amount of space between a
- /// <see cref="Border"/> and its child element.
- /// </summary>
- public Thickness Padding {
- get => padding;
- set {
- padding = value;
- OnBorderChanged ();
- }
- }
- /// <summary>
- /// Gets the rendered width of this element.
- /// </summary>
- 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);
- }
- }
- /// <summary>
- /// Gets the rendered height of this element.
- /// </summary>
- 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);
- }
- }
- /// <summary>
- /// Gets or sets the single child element of a <see cref="View"/>.
- /// </summary>
- 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;
- }
- /// <summary>
- /// Gets the parent <see cref="Child"/> parent if any.
- /// </summary>
- public View Parent { get => Child?.SuperView; }
- /// <summary>
- /// Gets or private sets by the <see cref="ToplevelContainer"/>
- /// </summary>
- public ToplevelContainer ChildContainer { get; private set; }
- /// <summary>
- /// Gets or sets the 3D effect around the <see cref="Border"/>.
- /// </summary>
- public bool Effect3D {
- get => effect3D;
- set {
- effect3D = value;
- OnBorderChanged ();
- }
- }
- /// <summary>
- /// Get or sets the offset start position for the <see cref="Effect3D"/>
- /// </summary>
- public Point Effect3DOffset {
- get => effect3DOffset;
- set {
- effect3DOffset = value;
- OnBorderChanged ();
- }
- }
- /// <summary>
- /// Gets or sets the color for the <see cref="Border"/>
- /// </summary>
- public Attribute? Effect3DBrush {
- get => effect3DBrush;
- set {
- effect3DBrush = value;
- OnBorderChanged ();
- }
- }
- /// <summary>
- /// The title to be displayed for this view.
- /// </summary>
- public ustring Title {
- get => title;
- set {
- title = value;
- OnBorderChanged ();
- }
- }
- /// <summary>
- /// Calculate the sum of the <see cref="Padding"/> and the <see cref="BorderThickness"/>
- /// </summary>
- /// <returns>The total of the <see cref="Border"/> <see cref="Thickness"/></returns>
- 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
- };
- }
- /// <summary>
- /// Drawn the <see cref="BorderThickness"/> more the <see cref="Padding"/>
- /// more the <see cref="Border.BorderStyle"/> and the <see cref="Effect3D"/>.
- /// </summary>
- /// <param name="view">The view to draw.</param>
- /// <param name="fill">If it will clear or not the content area.</param>
- 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);
- }
- }
- /// <summary>
- /// Same as <see cref="DrawContent"/> but drawing full frames for all borders.
- /// </summary>
- 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 = Math.Max (frame.Y, 0);
- 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.Max (Math.Min (frame.Y + borderThickness.Top, frame.Bottom), 0);
- 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.Max (Math.Min (frame.Y + borderThickness.Top, frame.Bottom), 0);
- 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 = Math.Max (frame.Y + borderThickness.Top, 0);
- 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 = Math.Max (frame.Y + sumThickness.Top, 0);
- 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 = Math.Max (frame.Y + sumThickness.Top, 0);
- 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);
- }
- /// <summary>
- /// Draws the view <see cref="Title"/> to the screen.
- /// </summary>
- /// <param name="view">The view.</param>
- 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);
- }
- }
- }
- /// <summary>
- /// Draws the <see cref="View.Text"/> to the screen.
- /// </summary>
- /// <param name="view">The view.</param>
- /// <param name="rect">The frame.</param>
- 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 ());
- }
- /// <summary>
- /// Invoke the <see cref="BorderChanged"/> event.
- /// </summary>
- public virtual void OnBorderChanged ()
- {
- BorderChanged?.Invoke (this);
- }
- }
- }
|