diff --git a/Terminal.Gui/Core/Border.cs b/Terminal.Gui/Core/Border.cs index 202c1ffbfc..8e772c856a 100644 --- a/Terminal.Gui/Core/Border.cs +++ b/Terminal.Gui/Core/Border.cs @@ -665,9 +665,9 @@ private void DrawChildBorder (Rect frame, bool fill = true) // Draw the upper BorderThickness for (int r = frame.Y - drawMarginFrame - sumThickness.Top; - r < frame.Y - drawMarginFrame - padding.Top; r++) { + r > 0 && 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++) { + c > 0 && c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -675,9 +675,9 @@ private void DrawChildBorder (Rect frame, bool fill = true) // Draw the left BorderThickness for (int r = frame.Y - drawMarginFrame - padding.Top; - r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) { + r > 0 && 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++) { + c > 0 && c < frame.X - drawMarginFrame - padding.Left; c++) { AddRuneAt (driver, c, r, ' '); } @@ -685,9 +685,9 @@ private void DrawChildBorder (Rect frame, bool fill = true) // Draw the right BorderThickness for (int r = frame.Y - drawMarginFrame - padding.Top; - r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) { + r > 0 && 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++) { + c > 0 && c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -695,9 +695,9 @@ private void DrawChildBorder (Rect frame, bool fill = true) // Draw the lower BorderThickness for (int r = frame.Bottom + drawMarginFrame + padding.Bottom; - r < Math.Min (frame.Bottom + drawMarginFrame + sumThickness.Bottom, driver.Rows); r++) { + r > 0 && r > 0 && 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++) { + c > 0 && c > 0 && c < Math.Min (frame.Right + drawMarginFrame + sumThickness.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -707,9 +707,9 @@ private void DrawChildBorder (Rect frame, bool fill = true) // Draw the upper Padding for (int r = frame.Y - drawMarginFrame - padding.Top; - r < frame.Y - drawMarginFrame; r++) { + r > 0 && r < frame.Y - drawMarginFrame; r++) { for (int c = frame.X - drawMarginFrame - padding.Left; - c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) { + c > 0 && c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -717,9 +717,9 @@ private void DrawChildBorder (Rect frame, bool fill = true) // Draw the left Padding for (int r = frame.Y - drawMarginFrame; - r < Math.Min (frame.Bottom + drawMarginFrame, driver.Rows); r++) { + r > 0 && r < Math.Min (frame.Bottom + drawMarginFrame, driver.Rows); r++) { for (int c = frame.X - drawMarginFrame - padding.Left; - c < frame.X - drawMarginFrame; c++) { + c > 0 && c < frame.X - drawMarginFrame; c++) { AddRuneAt (driver, c, r, ' '); } @@ -727,9 +727,9 @@ private void DrawChildBorder (Rect frame, bool fill = true) // Draw the right Padding for (int r = frame.Y - drawMarginFrame; - r < Math.Min (frame.Bottom + drawMarginFrame, driver.Rows); r++) { + r > 0 && 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++) { + c > 0 && c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -737,9 +737,9 @@ private void DrawChildBorder (Rect frame, bool fill = true) // Draw the lower Padding for (int r = frame.Bottom + drawMarginFrame; - r < Math.Min (frame.Bottom + drawMarginFrame + padding.Bottom, driver.Rows); r++) { + r > 0 && 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++) { + c > 0 && c < Math.Min (frame.Right + drawMarginFrame + padding.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -820,9 +820,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // Draw the upper BorderThickness for (int r = frame.Y; - r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) { + r > 0 && r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) { for (int c = frame.X; - c < Math.Min (frame.Right, driver.Cols); c++) { + c > 0 && c < Math.Min (frame.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -830,9 +830,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // 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++) { + r > 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++) { + c > 0 && c < Math.Min (frame.X + borderThickness.Left, frame.Right); c++) { AddRuneAt (driver, c, r, ' '); } @@ -840,9 +840,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // 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++) { + r > 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++) { + c > 0 && c < Math.Min (frame.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -850,9 +850,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // Draw the lower BorderThickness for (int r = Math.Max (frame.Bottom - borderThickness.Bottom, frame.Y); - r < Math.Min (frame.Bottom, driver.Rows); r++) { + r > 0 && r < Math.Min (frame.Bottom, driver.Rows); r++) { for (int c = frame.X; - c < Math.Min (frame.Right, driver.Cols); c++) { + c > 0 && c < Math.Min (frame.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -862,9 +862,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // Draw the upper Padding for (int r = frame.Y + borderThickness.Top; - r < Math.Min (frame.Y + sumThickness.Top, frame.Bottom - borderThickness.Bottom); r++) { + r > 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++) { + c > 0 && c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -872,9 +872,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // Draw the left Padding for (int r = frame.Y + sumThickness.Top; - r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) { + r > 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++) { + c > 0 && c < Math.Min (frame.X + sumThickness.Left, frame.Right - borderThickness.Right); c++) { AddRuneAt (driver, c, r, ' '); } @@ -882,9 +882,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // Draw the right Padding for (int r = frame.Y + sumThickness.Top; - r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) { + r > 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++) { + c > 0 && c < Math.Max (frame.Right - borderThickness.Right, frame.X + sumThickness.Left); c++) { AddRuneAt (driver, c, r, ' '); } @@ -892,9 +892,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // 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++) { + r > 0 && 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++) { + c > 0 && c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) { AddRuneAt (driver, c, r, ' '); } @@ -921,9 +921,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // Draw the upper Effect3D for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0); - r < frame.Y; r++) { + r > 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++) { + c > 0 && c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } @@ -931,9 +931,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // Draw the left Effect3D for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0); - r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) { + r > 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++) { + c > 0 && c < frame.X; c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } @@ -941,9 +941,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // Draw the right Effect3D for (int r = Math.Max (frame.Y + effect3DOffset.Y, 0); - r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) { + r > 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++) { + c > 0 && c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } @@ -951,9 +951,9 @@ private void DrawParentBorder (Rect frame, bool fill = true) // Draw the lower Effect3D for (int r = frame.Bottom; - r < Math.Min (frame.Bottom + effect3DOffset.Y, driver.Rows); r++) { + r > 0 && 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++) { + c > 0 && c < Math.Min (frame.Right + effect3DOffset.X, driver.Cols); c++) { AddRuneAt (driver, c, r, (Rune)driver.Contents [r, c, 0]); } diff --git a/Terminal.Gui/Core/PosDim.cs b/Terminal.Gui/Core/PosDim.cs index 1169fa0493..696607696b 100644 --- a/Terminal.Gui/Core/PosDim.cs +++ b/Terminal.Gui/Core/PosDim.cs @@ -52,7 +52,7 @@ internal override int Anchor (int width) public override string ToString () { - return $"Pos.PosFunc({function ()})"; + return $"PosFunc({function ()})"; } public override int GetHashCode () => function.GetHashCode (); @@ -85,7 +85,7 @@ internal override int Anchor (int width) public override string ToString () { - return $"Pos.Factor({factor})"; + return $"Factor({factor})"; } public override int GetHashCode () => factor.GetHashCode (); @@ -135,7 +135,7 @@ internal override int Anchor (int width) public override string ToString () { - return $"Pos.AnchorEnd(margin={n})"; + return $"AnchorEnd({n})"; } } @@ -174,7 +174,7 @@ internal override int Anchor (int width) public override string ToString () { - return "Pos.Center"; + return "Center"; } } @@ -209,7 +209,7 @@ internal class PosAbsolute : Pos { public override string ToString () { - return $"Pos.Absolute({n})"; + return $"Absolute({n})"; } internal override int Anchor (int width) @@ -244,7 +244,7 @@ public static Pos At (int n) internal class PosCombine : Pos { internal Pos left, right; - bool add; + internal bool add; public PosCombine (bool add, Pos left, Pos right) { this.left = left; @@ -264,7 +264,7 @@ internal override int Anchor (int width) public override string ToString () { - return $"Pos.Combine({left.ToString ()}{(add ? '+' : '-')}{right.ToString ()})"; + return $"Combine({left}{(add ? '+' : '-')}{right})"; } } @@ -345,7 +345,7 @@ public override string ToString () case 3: tside = "bottom"; break; default: tside = "unknown"; break; } - return $"Pos.View(side={tside}, target={Target.ToString ()})"; + return $"View({tside},{Target.ToString()})"; } public override int GetHashCode () => Target.GetHashCode (); @@ -442,7 +442,7 @@ internal override int Anchor (int width) public override string ToString () { - return $"Dim.DimFunc({function ()})"; + return $"DimFunc({function ()})"; } public override int GetHashCode () => function.GetHashCode (); @@ -482,7 +482,7 @@ public bool IsFromRemaining () public override string ToString () { - return $"Dim.Factor(factor={factor}, remaining={remaining})"; + return $"Factor({factor},{remaining})"; } public override int GetHashCode () => factor.GetHashCode (); @@ -522,7 +522,7 @@ internal class DimAbsolute : Dim { public override string ToString () { - return $"Dim.Absolute({n})"; + return $"Absolute({n})"; } internal override int Anchor (int width) @@ -541,7 +541,7 @@ internal class DimFill : Dim { public override string ToString () { - return $"Dim.Fill(margin={margin})"; + return $"Fill({margin})"; } internal override int Anchor (int width) @@ -613,7 +613,7 @@ internal override int Anchor (int width) public override string ToString () { - return $"Dim.Combine({left.ToString ()}{(add ? '+' : '-')}{right.ToString ()})"; + return $"Combine({left}{(add ? '+' : '-')}{right})"; } } @@ -691,7 +691,7 @@ public override string ToString () case 1: tside = "Width"; break; default: tside = "unknown"; break; } - return $"DimView(side={tside}, target={Target.ToString ()})"; + return $"DimView({tside},{Target.ToString ()})"; } public override int GetHashCode () => Target.GetHashCode (); diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 018cf1384f..e303bb343e 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Linq; using System.Reflection; using NStack; @@ -1109,8 +1110,15 @@ public void BringSubviewForward (View subview) /// public void Clear () { - var h = Frame.Height; - var w = Frame.Width; + Rect containerBounds = GetContainerBounds (); + Rect viewBounds = Bounds; + if (!containerBounds.IsEmpty) { + viewBounds.Width = Math.Min (viewBounds.Width, containerBounds.Width); + viewBounds.Height = Math.Min (viewBounds.Height, containerBounds.Height); + } + + var h = viewBounds.Height; + var w = viewBounds.Width; for (var line = 0; line < h; line++) { Move (0, line); for (var col = 0; col < w; col++) @@ -1518,7 +1526,7 @@ public virtual void Redraw (Rect bounds) } else if (ustring.IsNullOrEmpty (TextFormatter.Text) && (GetType ().IsNestedPublic && !IsOverridden (this, "Redraw") || GetType ().Name == "View") && (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded)) { - + Clear (); SetChildNeedsDisplay (); } @@ -1543,8 +1551,9 @@ public virtual void Redraw (Rect bounds) foreach (var view in subviews) { if (!view.NeedDisplay.IsEmpty || view.ChildNeedsDisplay || view.LayoutNeeded) { if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) { - if (view.LayoutNeeded) + if (view.LayoutNeeded) { view.LayoutSubviews (); + } // Draw the subview // Use the view's bounds (view-relative; Location will always be (0,0) @@ -2174,119 +2183,226 @@ View GetMostFocused (View view) } /// - /// Sets the View's to the relative coordinates if its container, given the for its container. + /// Sets the View's to the frame-relative coordinates if its container. The + /// container size and location are specified by and are relative to the + /// View's superview. /// - /// The screen-relative frame for the host. - /// - /// Reminder: is superview-relative; is view-relative. - /// - internal void SetRelativeLayout (Rect hostFrame) + /// The supserview-relative rectangle describing View's container (nominally the + /// same as this.SuperView.Frame). + internal void SetRelativeLayout (Rect superviewFrame) { - int actW, actH, actX, actY; - var s = Size.Empty; + int newX, newW, newY, newH; + var autosize = Size.Empty; if (AutoSize) { - s = GetAutoSize (); + // Note this is global to this function and used as such within the local functions defined + // below. In v2 AutoSize will be re-factored to not need to be dealt with in this function. + autosize = GetAutoSize (); } - if (x is Pos.PosCenter) { - if (width == null) { - actW = AutoSize ? s.Width : hostFrame.Width; - } else { - actW = width.Anchor (hostFrame.Width); - actW = AutoSize && s.Width > actW ? s.Width : actW; - } - actX = x.Anchor (hostFrame.Width - actW); - } else { - actX = x?.Anchor (hostFrame.Width) ?? 0; + // Returns the new dimension (width or height) and location (x or y) for the View given + // the superview's Frame.X or Frame.Y + // the superview's width or height + // the current Pos (View.X or View.Y) + // the current Dim (View.Width or View.Height) + (int newLocation, int newDimension) GetNewLocationAndDimension (int superviewLocation, int superviewDimension, Pos pos, Dim dim, int autosizeDimension) + { + int newDimension, newLocation; - actW = Math.Max (CalculateActualWidth (width, hostFrame, actX, s), 0); + switch (pos) { + case Pos.PosCenter: + if (dim == null) { + newDimension = AutoSize ? autosizeDimension : superviewDimension; + } else { + newDimension = dim.Anchor (superviewDimension); + newDimension = AutoSize && autosizeDimension > newDimension ? autosizeDimension : newDimension; + } + newLocation = pos.Anchor (superviewDimension - newDimension); + break; + + case Pos.PosCombine: + var combine = pos as Pos.PosCombine; + int left, right; + (left, newDimension) = GetNewLocationAndDimension (superviewLocation, superviewDimension, combine.left, dim, autosizeDimension); + (right, newDimension) = GetNewLocationAndDimension (superviewLocation, superviewDimension, combine.right, dim, autosizeDimension); + if (combine.add) { + newLocation = left + right; + } else { + newLocation = left - right; + } + newDimension = Math.Max (CalculateNewDimension (dim, newLocation, superviewDimension, autosizeDimension), 0); + break; + + case Pos.PosAbsolute: + case Pos.PosAnchorEnd: + case Pos.PosFactor: + case Pos.PosFunc: + case Pos.PosView: + default: + newLocation = pos?.Anchor (superviewDimension) ?? 0; + newDimension = Math.Max (CalculateNewDimension (dim, newLocation, superviewDimension, autosizeDimension), 0); + break; + } + return (newLocation, newDimension); } - if (y is Pos.PosCenter) { - if (height == null) { - actH = AutoSize ? s.Height : hostFrame.Height; - } else { - actH = height.Anchor (hostFrame.Height); - actH = AutoSize && s.Height > actH ? s.Height : actH; + // Recursively calculates the new dimension (width or height) of the given Dim given: + // the current location (x or y) + // the current dimennsion (width or height) + int CalculateNewDimension (Dim d, int location, int dimension, int autosize) + { + int newDimension; + switch (d) { + case null: + newDimension = AutoSize ? autosize : dimension; + break; + case Dim.DimCombine combine: + int leftNewDim = CalculateNewDimension (combine.left, location, dimension, autosize); + int rightNewDim = CalculateNewDimension (combine.right, location, dimension, autosize); + if (combine.add) { + newDimension = leftNewDim + rightNewDim; + } else { + newDimension = leftNewDim - rightNewDim; + } + newDimension = AutoSize && autosize > newDimension ? autosize : newDimension; + break; + + case Dim.DimFactor factor when !factor.IsFromRemaining (): + newDimension = d.Anchor (dimension); + newDimension = AutoSize && autosize > newDimension ? autosize : newDimension; + break; + + case Dim.DimFill: + default: + newDimension = Math.Max (d.Anchor (dimension - location), 0); + newDimension = AutoSize && autosize > newDimension ? autosize : newDimension; + break; } - actY = y.Anchor (hostFrame.Height - actH); - } else { - actY = y?.Anchor (hostFrame.Height) ?? 0; - actH = Math.Max (CalculateActualHight (height, hostFrame, actY, s), 0); + return newDimension; } - var r = new Rect (actX, actY, actW, actH); + + // horiztonal + (newX, newW) = GetNewLocationAndDimension (superviewFrame.X, superviewFrame.Width, x, Width, autosize.Width); + + // vertical + (newY, newH) = GetNewLocationAndDimension (superviewFrame.Y, superviewFrame.Height, y, Height, autosize.Height); + + var r = new Rect (newX, newY, newW, newH); if (Frame != r) { Frame = r; - if (!SetMinWidthHeight ()) + if (!SetMinWidthHeight ()) { TextFormatter.Size = GetBoundsTextFormatterSize (); + } } } - private int CalculateActualWidth (Dim width, Rect hostFrame, int actX, Size s) + /// + /// Event arguments for the event. + /// + public class LayoutEventArgs : EventArgs { + /// + /// The view-relative bounds of the before it was laid out. + /// + public Rect OldBounds { get; set; } + } + + /// + /// Fired after the View's method has completed. + /// + /// + /// Subscribe to this event to perform tasks when the has been resized or the layout has otherwise changed. + /// + public event Action LayoutStarted; + + /// + /// Raises the event. Called from before any subviews have been laid out. + /// + internal virtual void OnLayoutStarted (LayoutEventArgs args) { - int actW; - switch (width) { - case null: - actW = AutoSize ? s.Width : hostFrame.Width; - break; - case Dim.DimCombine combine: - int leftActW = CalculateActualWidth (combine.left, hostFrame, actX, s); - int rightActW = CalculateActualWidth (combine.right, hostFrame, actX, s); - if (combine.add) { - actW = leftActW + rightActW; - } else { - actW = leftActW - rightActW; + LayoutStarted?.Invoke (args); + } + + /// + /// Fired after the View's method has completed. + /// + /// + /// Subscribe to this event to perform tasks when the has been resized or the layout has otherwise changed. + /// + public event Action LayoutComplete; + + /// + /// Event called only once when the is being initialized for the first time. + /// Allows configurations and assignments to be performed before the being shown. + /// This derived from to allow notify all the views that are being initialized. + /// + public event EventHandler Initialized; + + /// + /// Raises the event. Called from before all sub-views have been laid out. + /// + internal virtual void OnLayoutComplete (LayoutEventArgs args) + { + LayoutComplete?.Invoke (args); + } + + internal void CollectPos (Pos pos, View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) + { + switch (pos) { + case Pos.PosView pv: + if (pv.Target != this) { + nEdges.Add ((pv.Target, from)); + } + foreach (var v in from.InternalSubviews) { + CollectAll (v, ref nNodes, ref nEdges); + } + return; + case Pos.PosCombine pc: + foreach (var v in from.InternalSubviews) { + CollectPos (pc.left, from, ref nNodes, ref nEdges); + CollectPos (pc.right, from, ref nNodes, ref nEdges); } - actW = AutoSize && s.Width > actW ? s.Width : actW; - break; - case Dim.DimFactor factor when !factor.IsFromRemaining (): - actW = width.Anchor (hostFrame.Width); - actW = AutoSize && s.Width > actW ? s.Width : actW; - break; - default: - actW = Math.Max (width.Anchor (hostFrame.Width - actX), 0); - actW = AutoSize && s.Width > actW ? s.Width : actW; break; } - - return actW; } - private int CalculateActualHight (Dim height, Rect hostFrame, int actY, Size s) + internal void CollectDim (Dim dim, View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) { - int actH; - switch (height) { - case null: - actH = AutoSize ? s.Height : hostFrame.Height; - break; - case Dim.DimCombine combine: - int leftActH = CalculateActualHight (combine.left, hostFrame, actY, s); - int rightActH = CalculateActualHight (combine.right, hostFrame, actY, s); - if (combine.add) { - actH = leftActH + rightActH; - } else { - actH = leftActH - rightActH; + switch (dim) { + case Dim.DimView dv: + if (dv.Target != this) { + nEdges.Add ((dv.Target, from)); + } + foreach (var v in from.InternalSubviews) { + CollectAll (v, ref nNodes, ref nEdges); + } + return; + case Dim.DimCombine dc: + foreach (var v in from.InternalSubviews) { + CollectDim (dc.left, from, ref nNodes, ref nEdges); + CollectDim (dc.right, from, ref nNodes, ref nEdges); } - actH = AutoSize && s.Height > actH ? s.Height : actH; - break; - case Dim.DimFactor factor when !factor.IsFromRemaining (): - actH = height.Anchor (hostFrame.Height); - actH = AutoSize && s.Height > actH ? s.Height : actH; - break; - default: - actH = Math.Max (height.Anchor (hostFrame.Height - actY), 0); - actH = AutoSize && s.Height > actH ? s.Height : actH; break; } + } - return actH; + internal void CollectAll (View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) + { + foreach (var v in from.InternalSubviews) { + nNodes.Add (v); + if (v.layoutStyle != LayoutStyle.Computed) { + continue; + } + CollectPos (v.X, v, ref nNodes, ref nEdges); + CollectPos (v.Y, v, ref nNodes, ref nEdges); + CollectDim (v.Width, v, ref nNodes, ref nEdges); + CollectDim (v.Height, v, ref nNodes, ref nEdges); + } } // https://en.wikipedia.org/wiki/Topological_sorting - List TopologicalSort (IEnumerable nodes, ICollection<(View From, View To)> edges) + internal static List TopologicalSort (View superView, IEnumerable nodes, ICollection<(View From, View To)> edges) { var result = new List (); @@ -2299,7 +2415,7 @@ List TopologicalSort (IEnumerable nodes, ICollection<(View From, Vie noEdgeNodes.Remove (n); // add n to tail of L - if (n != this?.SuperView) + if (n != superView) result.Add (n); // for each node m with an edge e from n to m do @@ -2310,7 +2426,7 @@ List TopologicalSort (IEnumerable nodes, ICollection<(View From, Vie edges.Remove (e); // if m has no other incoming edges then - if (edges.All (me => !me.To.Equals (m)) && m != this?.SuperView) { + if (edges.All (me => !me.To.Equals (m)) && m != superView) { // insert m into S noEdgeNodes.Add (m); } @@ -2321,65 +2437,16 @@ List TopologicalSort (IEnumerable nodes, ICollection<(View From, Vie (var from, var to) = edges.First (); if (from != Application.Top) { if (!ReferenceEquals (from, to)) { - throw new InvalidOperationException ($"TopologicalSort (for Pos/Dim) cannot find {from} linked with {to}. Did you forget to add it to {this}?"); + throw new InvalidOperationException ($"TopologicalSort (for Pos/Dim) cannot find {from} linked with {to}. Did you forget to add it to {superView}?"); } else { - throw new InvalidOperationException ("TopologicalSort encountered a recursive cycle in the relative Pos/Dim in the views of " + this); + throw new InvalidOperationException ("TopologicalSort encountered a recursive cycle in the relative Pos/Dim in the views of " + superView); } } } - // return L (a topologically sorted order) return result; - } - - /// - /// Event arguments for the event. - /// - public class LayoutEventArgs : EventArgs { - /// - /// The view-relative bounds of the before it was laid out. - /// - public Rect OldBounds { get; set; } - } - - /// - /// Fired after the View's method has completed. - /// - /// - /// Subscribe to this event to perform tasks when the has been resized or the layout has otherwise changed. - /// - public event Action LayoutStarted; - - /// - /// Raises the event. Called from before any subviews have been laid out. - /// - internal virtual void OnLayoutStarted (LayoutEventArgs args) - { - LayoutStarted?.Invoke (args); - } - - /// - /// Fired after the View's method has completed. - /// - /// - /// Subscribe to this event to perform tasks when the has been resized or the layout has otherwise changed. - /// - public event Action LayoutComplete; - - /// - /// Event called only once when the is being initialized for the first time. - /// Allows configurations and assignments to be performed before the being shown. - /// This derived from to allow notify all the views that are being initialized. - /// - public event EventHandler Initialized; + } // TopologicalSort - /// - /// Raises the event. Called from before all sub-views have been laid out. - /// - internal virtual void OnLayoutComplete (LayoutEventArgs args) - { - LayoutComplete?.Invoke (args); - } /// /// Invoked when a view starts executing or when the dimensions of the view have changed, for example in @@ -2399,69 +2466,11 @@ public virtual void LayoutSubviews () TextFormatter.Size = GetBoundsTextFormatterSize (); - // Sort out the dependencies of the X, Y, Width, Height properties var nodes = new HashSet (); var edges = new HashSet<(View, View)> (); - - void CollectPos (Pos pos, View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) - { - switch (pos) { - case Pos.PosView pv: - if (pv.Target != this) { - nEdges.Add ((pv.Target, from)); - } - foreach (var v in from.InternalSubviews) { - CollectAll (v, ref nNodes, ref nEdges); - } - return; - case Pos.PosCombine pc: - foreach (var v in from.InternalSubviews) { - CollectPos (pc.left, from, ref nNodes, ref nEdges); - CollectPos (pc.right, from, ref nNodes, ref nEdges); - } - break; - } - } - - void CollectDim (Dim dim, View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) - { - switch (dim) { - case Dim.DimView dv: - if (dv.Target != this) { - nEdges.Add ((dv.Target, from)); - } - foreach (var v in from.InternalSubviews) { - CollectAll (v, ref nNodes, ref nEdges); - } - return; - case Dim.DimCombine dc: - foreach (var v in from.InternalSubviews) { - CollectDim (dc.left, from, ref nNodes, ref nEdges); - CollectDim (dc.right, from, ref nNodes, ref nEdges); - } - break; - } - } - - void CollectAll (View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) - { - foreach (var v in from.InternalSubviews) { - nNodes.Add (v); - if (v.layoutStyle != LayoutStyle.Computed) { - continue; - } - CollectPos (v.X, v, ref nNodes, ref nEdges); - CollectPos (v.Y, v, ref nNodes, ref nEdges); - CollectDim (v.Width, v, ref nNodes, ref nEdges); - CollectDim (v.Height, v, ref nNodes, ref nEdges); - } - } - CollectAll (this, ref nodes, ref edges); - - var ordered = TopologicalSort (nodes, edges); - + var ordered = View.TopologicalSort (SuperView, nodes, edges); foreach (var v in ordered) { if (v.LayoutStyle == LayoutStyle.Computed) { v.SetRelativeLayout (Frame); @@ -2471,9 +2480,12 @@ void CollectAll (View from, ref HashSet nNodes, ref HashSet<(View, View)> v.LayoutNeeded = false; } + // If our SuperView is Application.Top and the layoutstyle is Computed it's a special-cass. + // Use SetRelativeaLayout with the Frame of the Application.Top if (SuperView != null && SuperView == Application.Top && LayoutNeeded && ordered.Count == 0 && LayoutStyle == LayoutStyle.Computed) { - SetRelativeLayout (SuperView.Frame); + Debug.Assert (Application.Top.Frame.Location == Point.Empty); + SetRelativeLayout (Application.Top.Frame); } LayoutNeeded = false; @@ -2756,9 +2768,11 @@ bool SetWidthHeight (Size nBounds) } /// - /// Gets the size to fit all text if is true. + /// Gets the dimensions required to fit using the text specified by the + /// property and accounting for any characters. + /// . /// - /// The + /// The required to fit the text. public Size GetAutoSize () { var rect = TextFormatter.CalcRect (Bounds.X, Bounds.Y, TextFormatter.Text, TextFormatter.Direction); @@ -2793,10 +2807,11 @@ bool IsValidAutoSizeHeight (Dim height) } /// - /// Get the width or height of the length. + /// Gets the width or height of the characters in the property. /// - /// if is the width (default) if is the height. - /// The length of the . + /// If (the default) the width required for the hotkey specifier is returned. Otherwise the height is returned. + /// The number of characters required for the . If the text direction specified + /// by does not match the parameter, 0 is returned. public int GetHotKeySpecifierLength (bool isWidth = true) { if (isWidth) { @@ -2811,7 +2826,7 @@ public int GetHotKeySpecifierLength (bool isWidth = true) } /// - /// Gets the bounds size from a . + /// Gets the minus the size required for the . /// /// The bounds size minus the length. public Size GetTextFormatterBoundsSize () diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index 0bab49cbfd..a390fe83fd 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -57,6 +57,7 @@ net472;netstandard2.0;net6.0 + 9 Terminal.Gui Terminal.Gui bin\Release\Terminal.Gui.xml diff --git a/UICatalog/Scenario.cs b/UICatalog/Scenario.cs index e26ce29116..ae06c151c9 100644 --- a/UICatalog/Scenario.cs +++ b/UICatalog/Scenario.cs @@ -186,8 +186,8 @@ public virtual void Setup () } /// - /// Runs the . Override to start the using a different than `Top`. - /// + /// Runs the . Override to start the + /// using a different than `Top`. /// /// /// Overrides that do not call the base., must call before returning. diff --git a/UICatalog/Scenarios/ASCIICustomButton.cs b/UICatalog/Scenarios/ASCIICustomButton.cs deleted file mode 100644 index 77cacf8b95..0000000000 --- a/UICatalog/Scenarios/ASCIICustomButton.cs +++ /dev/null @@ -1,313 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Terminal.Gui; - -namespace UICatalog.Scenarios { - [ScenarioMetadata (Name: "ASCIICustomButtonTest", Description: "ASCIICustomButton sample")] - [ScenarioCategory ("Controls")] - public class ASCIICustomButtonTest : Scenario { - private static bool smallerWindow; - private ScrollViewTestWindow scrollViewTestWindow; - private MenuItem miSmallerWindow; - - public override void Init (ColorScheme colorScheme) - { - Application.Init (); - scrollViewTestWindow = new ScrollViewTestWindow (); - var menu = new MenuBar (new MenuBarItem [] { - new MenuBarItem("Window Size", new MenuItem [] { - miSmallerWindow = new MenuItem ("Smaller Window", "", ChangeWindowSize) { - CheckType = MenuItemCheckStyle.Checked - }, - null, - new MenuItem("Quit", "",() => Application.RequestStop(),null,null, Key.Q | Key.CtrlMask) - }) - }); - Application.Top.Add (menu, scrollViewTestWindow); - Application.Run (); - } - - private void ChangeWindowSize () - { - smallerWindow = miSmallerWindow.Checked = !miSmallerWindow.Checked; - scrollViewTestWindow.Dispose (); - Application.Top.Remove (scrollViewTestWindow); - scrollViewTestWindow = new ScrollViewTestWindow (); - Application.Top.Add (scrollViewTestWindow); - } - - public override void Run () - { - } - - public class ASCIICustomButton : Button { - public string Description => $"Description of: {id}"; - - public event Action PointerEnter; - - private Label fill; - private FrameView border; - private string id; - - public ASCIICustomButton (string text, Pos x, Pos y, int width, int height) : base (text) - { - CustomInitialize ("", text, x, y, width, height); - } - - public ASCIICustomButton (string id, string text, Pos x, Pos y, int width, int height) : base (text) - { - CustomInitialize (id, text, x, y, width, height); - } - - private void CustomInitialize (string id, string text, Pos x, Pos y, int width, int height) - { - this.id = id; - X = x; - Y = y; - - Frame = new Rect { - Width = width, - Height = height - }; - - border = new FrameView () { - Width = width, - Height = height - }; - - AutoSize = false; - - var fillText = new System.Text.StringBuilder (); - for (int i = 0; i < Bounds.Height; i++) { - if (i > 0) { - fillText.AppendLine (""); - } - for (int j = 0; j < Bounds.Width; j++) { - fillText.Append ("█"); - } - } - - fill = new Label (fillText.ToString ()) { - Visible = false, - CanFocus = false - }; - - var title = new Label (text) { - X = Pos.Center (), - Y = Pos.Center (), - }; - - border.MouseClick += This_MouseClick; - border.Subviews [0].MouseClick += This_MouseClick; - fill.MouseClick += This_MouseClick; - title.MouseClick += This_MouseClick; - - Add (border, fill, title); - } - - private void This_MouseClick (MouseEventArgs obj) - { - OnMouseEvent (obj.MouseEvent); - } - - public override bool OnMouseEvent (MouseEvent mouseEvent) - { - Debug.WriteLine ($"{mouseEvent.Flags}"); - if (mouseEvent.Flags == MouseFlags.Button1Clicked) { - if (!HasFocus && SuperView != null) { - if (!SuperView.HasFocus) { - SuperView.SetFocus (); - } - SetFocus (); - SetNeedsDisplay (); - } - - OnClicked (); - return true; - } - return base.OnMouseEvent (mouseEvent); - } - - public override bool OnEnter (View view) - { - border.Visible = false; - fill.Visible = true; - PointerEnter.Invoke (this); - view = this; - return base.OnEnter (view); - } - - public override bool OnLeave (View view) - { - border.Visible = true; - fill.Visible = false; - if (view == null) - view = this; - return base.OnLeave (view); - } - } - - public class ScrollViewTestWindow : Window { - private List