diff --git a/Terminal.Gui/Drawing/Thickness.cs b/Terminal.Gui/Drawing/Thickness.cs
index cb6fe7a8f..0950f872b 100644
--- a/Terminal.Gui/Drawing/Thickness.cs
+++ b/Terminal.Gui/Drawing/Thickness.cs
@@ -1,4 +1,5 @@
-using System.Numerics;
+#nullable enable
+using System.Numerics;
using System.Text.Json.Serialization;
namespace Terminal.Gui;
@@ -15,10 +16,7 @@ namespace Terminal.Gui;
/// with the thickness widths subtracted.
///
///
-/// Use the helper API ( to draw the frame with the specified thickness.
-///
-///
-/// Thickness uses intenrally. As a result, there is a potential precision loss for very
+/// Thickness uses internally. As a result, there is a potential precision loss for very
/// large numbers. This is typically not an issue for UI dimensions but could be relevant in other contexts.
///
///
@@ -91,7 +89,7 @@ public record struct Thickness
///
/// The diagnostics label to draw on the bottom of the .
/// The inner rectangle remaining to be drawn.
- public Rectangle Draw (Rectangle rect, ViewDiagnosticFlags diagnosticFlags = ViewDiagnosticFlags.Off, string label = null)
+ public Rectangle Draw (Rectangle rect, ViewDiagnosticFlags diagnosticFlags = ViewDiagnosticFlags.Off, string? label = null)
{
if (rect.Size.Width < 1 || rect.Size.Height < 1)
{
@@ -134,26 +132,26 @@ public record struct Thickness
if (Right > 0)
{
Application.Driver?.FillRect (
- rect with
- {
- X = Math.Max (0, rect.X + rect.Width - Right),
- Width = Math.Min (rect.Width, Right)
- },
- rightChar
- );
+ rect with
+ {
+ X = Math.Max (0, rect.X + rect.Width - Right),
+ Width = Math.Min (rect.Width, Right)
+ },
+ rightChar
+ );
}
// Draw the Bottom side
if (Bottom > 0)
{
Application.Driver?.FillRect (
- rect with
- {
- Y = rect.Y + Math.Max (0, rect.Height - Bottom),
- Height = Bottom
- },
- bottomChar
- );
+ rect with
+ {
+ Y = rect.Y + Math.Max (0, rect.Height - Bottom),
+ Height = Bottom
+ },
+ bottomChar
+ );
}
if (diagnosticFlags.HasFlag (ViewDiagnosticFlags.Ruler))
@@ -192,6 +190,7 @@ public record struct Thickness
{
// Draw the diagnostics label on the bottom
string text = label is null ? string.Empty : $"{label} {this}";
+
var tf = new TextFormatter
{
Text = text,
diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs
index 723e1c906..f88d932a0 100644
--- a/Terminal.Gui/View/Adornment/Border.cs
+++ b/Terminal.Gui/View/Adornment/Border.cs
@@ -483,8 +483,8 @@ public class Border : Adornment
Point parentLoc = Parent.SuperView?.ScreenToViewport (new (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y))
?? mouseEvent.ScreenPosition;
- int minHeight = Thickness.Vertical + Parent!.Margin.Thickness.Bottom;
- int minWidth = Thickness.Horizontal + Parent!.Margin.Thickness.Right;
+ int minHeight = Thickness.Vertical + Parent!.Margin!.Thickness.Bottom;
+ int minWidth = Thickness.Horizontal + Parent!.Margin!.Thickness.Right;
// TODO: This code can be refactored to be more readable and maintainable.
switch (_arranging)
@@ -1072,7 +1072,7 @@ public class Border : Adornment
NoPadding = true,
ShadowStyle = ShadowStyle.None,
Text = $"{Glyphs.SizeVertical}",
- X = Pos.Center () + Parent!.Margin.Thickness.Horizontal,
+ X = Pos.Center () + Parent!.Margin!.Thickness.Horizontal,
Y = 0,
Visible = false,
Data = ViewArrangement.TopResizable
@@ -1095,7 +1095,7 @@ public class Border : Adornment
ShadowStyle = ShadowStyle.None,
Text = $"{Glyphs.SizeHorizontal}",
X = Pos.AnchorEnd (),
- Y = Pos.Center () + Parent!.Margin.Thickness.Vertical / 2,
+ Y = Pos.Center () + Parent!.Margin!.Thickness.Vertical / 2,
Visible = false,
Data = ViewArrangement.RightResizable
};
@@ -1117,7 +1117,7 @@ public class Border : Adornment
ShadowStyle = ShadowStyle.None,
Text = $"{Glyphs.SizeHorizontal}",
X = 0,
- Y = Pos.Center () + Parent!.Margin.Thickness.Vertical / 2,
+ Y = Pos.Center () + Parent!.Margin!.Thickness.Vertical / 2,
Visible = false,
Data = ViewArrangement.LeftResizable
};
@@ -1138,7 +1138,7 @@ public class Border : Adornment
NoPadding = true,
ShadowStyle = ShadowStyle.None,
Text = $"{Glyphs.SizeVertical}",
- X = Pos.Center () + Parent!.Margin.Thickness.Horizontal / 2,
+ X = Pos.Center () + Parent!.Margin!.Thickness.Horizontal / 2,
Y = Pos.AnchorEnd (),
Visible = false,
Data = ViewArrangement.BottomResizable
diff --git a/Terminal.Gui/View/Adornment/Margin.cs b/Terminal.Gui/View/Adornment/Margin.cs
index fab6f2ffa..4387f12a3 100644
--- a/Terminal.Gui/View/Adornment/Margin.cs
+++ b/Terminal.Gui/View/Adornment/Margin.cs
@@ -275,13 +275,13 @@ public class Margin : Adornment
{
case ShadowStyle.Transparent:
// BUGBUG: This doesn't work right for all Border.Top sizes - Need an API on Border that gives top-right location of line corner.
- _rightShadow.Y = Parent!.Border.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0;
+ _rightShadow.Y = Parent!.Border!.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0;
break;
case ShadowStyle.Opaque:
// BUGBUG: This doesn't work right for all Border.Top sizes - Need an API on Border that gives top-right location of line corner.
- _rightShadow.Y = Parent!.Border.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0;
+ _rightShadow.Y = Parent!.Border!.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0;
_bottomShadow.X = Parent.Border.Thickness.Left > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).X + 1 : 0;
break;
diff --git a/Terminal.Gui/View/DrawEventArgs.cs b/Terminal.Gui/View/DrawEventArgs.cs
index ff54c166e..f3cfc7747 100644
--- a/Terminal.Gui/View/DrawEventArgs.cs
+++ b/Terminal.Gui/View/DrawEventArgs.cs
@@ -20,9 +20,6 @@ public class DrawEventArgs : CancelEventArgs
OldViewport = oldViewport;
}
- /// If set to true, the draw operation will be canceled, if applicable.
- public bool Cancel { get; set; }
-
/// Gets the Content-relative rectangle describing the old visible viewport into the .
public Rectangle OldViewport { get; }
diff --git a/Terminal.Gui/View/View.Adornments.cs b/Terminal.Gui/View/View.Adornments.cs
index 0775cdcd3..f5f9c0e89 100644
--- a/Terminal.Gui/View/View.Adornments.cs
+++ b/Terminal.Gui/View/View.Adornments.cs
@@ -1,4 +1,5 @@
-namespace Terminal.Gui;
+#nullable enable
+namespace Terminal.Gui;
public partial class View // Adornments
{
@@ -7,9 +8,10 @@ public partial class View // Adornments
///
private void SetupAdornments ()
{
- //// TODO: Move this to Adornment as a static factory method
+ // TODO: Move this to Adornment as a static factory method
if (this is not Adornment)
{
+ // TODO: Make the Adornments Lazy and only create them when needed
Margin = new (this);
Border = new (this);
Padding = new (this);
@@ -61,7 +63,7 @@ public partial class View // Adornments
/// and its .
///
///
- public Margin Margin { get; private set; }
+ public Margin? Margin { get; private set; }
private ShadowStyle _shadowStyle;
@@ -117,7 +119,7 @@ public partial class View // Adornments
/// and its .
///
///
- public Border Border { get; private set; }
+ public Border? Border { get; private set; }
/// Gets or sets whether the view has a one row/col thick border.
///
@@ -130,6 +132,10 @@ public partial class View // Adornments
/// Setting this property to is equivalent to setting 's
/// to `0` and to .
///
+ ///
+ /// Calls and raises , which allows change
+ /// to be cancelled.
+ ///
/// For more advanced customization of the view's border, manipulate see directly.
///
public LineStyle BorderStyle
@@ -150,32 +156,32 @@ public partial class View // Adornments
return;
}
+ BorderStyleChanging?.Invoke (this, e);
+
+ if (e.Cancel)
+ {
+ return;
+ }
+
SetBorderStyle (e.NewValue);
SetAdornmentFrames ();
SetNeedsLayout ();
-
}
}
///
- /// Called when the is changing. Invokes , which allows the
- /// event to be cancelled.
+ /// Called when the is changing.
///
///
- /// Override to prevent the from changing.
+ /// Set e.Cancel to true to prevent the from changing.
///
///
- protected virtual bool OnBorderStyleChanging (CancelEventArgs e)
- {
- if (Border is null)
- {
- return false;
- }
+ protected virtual bool OnBorderStyleChanging (CancelEventArgs e) { return false; }
- BorderStyleChanging?.Invoke (this, e);
-
- return e.Cancel;
- }
+ ///
+ /// Fired when the is changing. Allows the event to be cancelled.
+ ///
+ public event EventHandler>? BorderStyleChanging;
///
/// Sets the of the view to the specified value.
@@ -198,25 +204,19 @@ public partial class View // Adornments
{
if (value != LineStyle.None)
{
- if (Border.Thickness == Thickness.Empty)
+ if (Border!.Thickness == Thickness.Empty)
{
Border.Thickness = new (1);
}
}
else
{
- Border.Thickness = new (0);
+ Border!.Thickness = new (0);
}
Border.LineStyle = value;
}
- ///
- /// Fired when the is changing. Allows the event to be cancelled.
- ///
- [CanBeNull]
- public event EventHandler> BorderStyleChanging;
-
///
/// The inside of the view that offsets the
/// from the .
@@ -232,7 +232,7 @@ public partial class View // Adornments
/// and its .
///
///
- public Padding Padding { get; private set; }
+ public Padding? Padding { get; private set; }
///
/// Gets the thickness describing the sum of the Adornments' thicknesses.
@@ -245,12 +245,24 @@ public partial class View // Adornments
/// A thickness that describes the sum of the Adornments' thicknesses.
public Thickness GetAdornmentsThickness ()
{
- if (Margin is null)
+ Thickness result = Thickness.Empty;
+
+ if (Margin is { })
{
- return Thickness.Empty;
+ result += Margin.Thickness;
}
- return Margin.Thickness + Border.Thickness + Padding.Thickness;
+ if (Border is { })
+ {
+ result += Border.Thickness;
+ }
+
+ if (Padding is { })
+ {
+ result += Padding.Thickness;
+ }
+
+ return result;
}
/// Sets the Frame's of the Margin, Border, and Padding.
@@ -262,13 +274,19 @@ public partial class View // Adornments
return;
}
- if (Margin is null)
+ if (Margin is { })
{
- return; // CreateAdornments () has not been called yet
+ Margin!.Frame = Rectangle.Empty with { Size = Frame.Size };
}
- Margin.Frame = Rectangle.Empty with { Size = Frame.Size };
- Border.Frame = Margin.Thickness.GetInside (Margin.Frame);
- Padding.Frame = Border.Thickness.GetInside (Border.Frame);
+ if (Border is { } && Margin is { })
+ {
+ Border!.Frame = Margin!.Thickness.GetInside (Margin!.Frame);
+ }
+
+ if (Padding is { } && Border is { })
+ {
+ Padding!.Frame = Border!.Thickness.GetInside (Border!.Frame);
+ }
}
}
diff --git a/Terminal.Gui/View/View.Arrangement.cs b/Terminal.Gui/View/View.Arrangement.cs
index aec9e6553..e11b1d389 100644
--- a/Terminal.Gui/View/View.Arrangement.cs
+++ b/Terminal.Gui/View/View.Arrangement.cs
@@ -1,4 +1,5 @@
-namespace Terminal.Gui;
+#nullable enable
+namespace Terminal.Gui;
public partial class View
{
diff --git a/Terminal.Gui/View/View.Drawing.Clipping.cs b/Terminal.Gui/View/View.Drawing.Clipping.cs
index 9d4058cd3..2996751f9 100644
--- a/Terminal.Gui/View/View.Drawing.Clipping.cs
+++ b/Terminal.Gui/View/View.Drawing.Clipping.cs
@@ -1,6 +1,4 @@
#nullable enable
-using static Unix.Terminal.Curses;
-
namespace Terminal.Gui;
public partial class View
@@ -13,21 +11,20 @@ public partial class View
/// There is a single clip region for the entire application.
///
///
- /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first.
+ /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is
+ /// recommended to clone it first.
///
///
/// The current Clip.
- public static Region? GetClip ()
- {
- return Application.Driver?.Clip;
- }
+ public static Region? GetClip () { return Application.Driver?.Clip; }
///
/// Sets the Clip to the specified region.
///
///
///
- /// There is a single clip region for the entire application. This method sets the clip region to the specified region.
+ /// There is a single clip region for the entire application. This method sets the clip region to the specified
+ /// region.
///
///
///
@@ -47,7 +44,8 @@ public partial class View
/// There is a single clip region for the entire application. This method sets the clip region to the screen.
///
///
- /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first.
+ /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is
+ /// recommended to clone it first.
///
///
///
@@ -56,6 +54,7 @@ public partial class View
public static Region? SetClipToScreen ()
{
Region? previous = GetClip ();
+
if (Driver is { })
{
Driver.Clip = new (Application.Screen);
@@ -65,7 +64,7 @@ public partial class View
}
///
- /// Removes the specified rectangle from the Clip.
+ /// Removes the specified rectangle from the Clip.
///
///
///
@@ -73,17 +72,15 @@ public partial class View
///
///
///
- public static void ExcludeFromClip (Rectangle rectangle)
- {
- Driver?.Clip?.Exclude (rectangle);
- }
+ public static void ExcludeFromClip (Rectangle rectangle) { Driver?.Clip?.Exclude (rectangle); }
///
/// Changes the Clip to the intersection of the current Clip and the of this View.
///
///
///
- /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first.
+ /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is
+ /// recommended to clone it first.
///
///
///
@@ -99,6 +96,7 @@ public partial class View
Region previous = GetClip () ?? new (Application.Screen);
Region frameRegion = previous.Clone ();
+
// Translate viewportRegion to screen-relative coords
Rectangle screenRect = FrameToScreen ();
frameRegion.Intersect (screenRect);
@@ -109,7 +107,7 @@ public partial class View
frameRegion.Exclude (adornment.Thickness.GetInside (Frame));
}
- View.SetClip (frameRegion);
+ SetClip (frameRegion);
return previous;
}
@@ -125,11 +123,12 @@ public partial class View
/// If has set, clipping will be
/// applied to just the visible content area.
///
- ///
- ///
- /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first.
- ///
- ///
+ ///
+ ///
+ /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it
+ /// is recommended to clone it first.
+ ///
+ ///
///
///
/// The current Clip, which can be then re-applied
@@ -161,7 +160,7 @@ public partial class View
viewportRegion?.Exclude (adornment.Thickness.GetInside (viewport));
}
- View.SetClip (viewportRegion);
+ SetClip (viewportRegion);
return previous;
}
diff --git a/Terminal.Gui/View/View.Drawing.Primitives.cs b/Terminal.Gui/View/View.Drawing.Primitives.cs
index f76162c94..3ddc9de6f 100644
--- a/Terminal.Gui/View/View.Drawing.Primitives.cs
+++ b/Terminal.Gui/View/View.Drawing.Primitives.cs
@@ -2,8 +2,6 @@
public partial class View
{
- #region Drawing Primitives
-
/// Moves the drawing cursor to the specified -relative location in the view.
///
///
@@ -121,8 +119,6 @@ public partial class View
Attribute prev = SetAttribute (new (color ?? GetNormalColor ().Background));
Driver.FillRect (toClear);
SetAttribute (prev);
- View.SetClip (prevClip);
+ SetClip (prevClip);
}
-
- #endregion Drawing Primitives
}
diff --git a/Terminal.Gui/View/View.Drawing.cs b/Terminal.Gui/View/View.Drawing.cs
index f3c6cceaf..158d37beb 100644
--- a/Terminal.Gui/View/View.Drawing.cs
+++ b/Terminal.Gui/View/View.Drawing.cs
@@ -1,13 +1,10 @@
#nullable enable
-//#define HACK_DRAW_OVERLAPPED
using System.ComponentModel;
-using System.Diagnostics;
namespace Terminal.Gui;
public partial class View // Drawing APIs
{
-
internal static void Draw (IEnumerable views, bool force)
{
IEnumerable viewsArray = views as View [] ?? views.ToArray ();
@@ -25,7 +22,6 @@ public partial class View // Drawing APIs
Margin.DrawMargins (viewsArray);
}
-
///
/// Draws the view if it needs to be drawn.
///
@@ -47,11 +43,12 @@ public partial class View // Drawing APIs
}
Region? saved = GetClip ();
+
if (NeedsDraw || SubViewNeedsDraw)
{
saved = ClipFrame ();
DoDrawBorderAndPadding ();
- View.SetClip (saved);
+ SetClip (saved);
// By default, we clip to the viewport preventing drawing outside the viewport
// We also clip to the content, but if a developer wants to draw outside the viewport, they can do
@@ -108,7 +105,7 @@ public partial class View // Drawing APIs
DoDrawComplete ();
// QUESTION: Should this go before DoDrawComplete? What is more correct?
- View.SetClip (saved);
+ SetClip (saved);
// Exclude this view (not including Margin) from the Clip
if (this is not Adornment && GetClip () is { })
@@ -221,7 +218,6 @@ public partial class View // Drawing APIs
SetNormalAttribute ();
}
-
///
/// Called when the normal attribute for the View is to be set. This is called before the View is drawn.
///
@@ -245,13 +241,12 @@ public partial class View // Drawing APIs
}
}
-
#endregion
+
#region ClearViewport
private void DoClearViewport ()
{
-
if (OnClearingViewport ())
{
return;
@@ -329,7 +324,6 @@ public partial class View // Drawing APIs
private void DoDrawText ()
{
-
if (OnDrawingText ())
{
return;
@@ -513,14 +507,15 @@ public partial class View // Drawing APIs
///
/// Gets or sets whether this View will use it's SuperView's for rendering any
/// lines. If the rendering of any borders drawn by this Frame will be done by its parent's
- /// SuperView. If (the default) this View's method will be
+ /// SuperView. If (the default) this View's method will
+ /// be
/// called to render the borders.
///
public virtual bool SuperViewRendersLineCanvas { get; set; } = false;
///
- /// Causes the contents of to be drawn.
- /// If is true, only the
+ /// Causes the contents of to be drawn.
+ /// If is true, only the
/// of this view's subviews will be rendered. If is
/// false (the default), this method will cause the to be rendered.
///
@@ -577,6 +572,7 @@ public partial class View // Drawing APIs
LineCanvas.Clear ();
}
}
+
#endregion DrawLineCanvas
#region DrawComplete
@@ -757,6 +753,7 @@ public partial class View // Drawing APIs
{
Padding?.ClearNeedsDraw ();
}
+
foreach (View subview in Subviews)
{
subview.ClearNeedsDraw ();
diff --git a/Terminal.Gui/View/View.Layout.cs b/Terminal.Gui/View/View.Layout.cs
index 0c97e8d66..bc4ee3dbe 100644
--- a/Terminal.Gui/View/View.Layout.cs
+++ b/Terminal.Gui/View/View.Layout.cs
@@ -1,7 +1,5 @@
#nullable enable
using System.Diagnostics;
-using Microsoft.CodeAnalysis;
-using static Unix.Terminal.Curses;
namespace Terminal.Gui;
@@ -33,10 +31,12 @@ public partial class View // Layout APIs
/// .
///
///
- /// Setting Frame will set , , , and to absoulte values.
+ /// Setting Frame will set , , , and to
+ /// absoulte values.
///
///
- /// Changing this property will result in and to be set, resulting in the
+ /// Changing this property will result in and to be set,
+ /// resulting in the
/// view being laid out and redrawn as appropriate in the next iteration of the .
///
///
@@ -44,10 +44,11 @@ public partial class View // Layout APIs
{
get
{
- if (_needsLayout)
+ if (NeedsLayout)
{
//Debug.WriteLine("Frame_get with _layoutNeeded");
}
+
return _frame ?? Rectangle.Empty;
}
set
@@ -193,7 +194,8 @@ public partial class View // Layout APIs
/// laid out (e.g. has been called).
///
///
- /// Changing this property will result in and to be set, resulting in the
+ /// Changing this property will result in and to be set,
+ /// resulting in the
/// view being laid out and redrawn as appropriate in the next iteration of the .
///
///
@@ -219,7 +221,6 @@ public partial class View // Layout APIs
private Pos _y = Pos.Absolute (0);
-
/// Gets or sets the Y position for the view (the row).
/// The object representing the Y position.
///
@@ -236,7 +237,8 @@ public partial class View // Layout APIs
/// laid out (e.g. has been called).
///
///
- /// Changing this property will result in and to be set, resulting in the
+ /// Changing this property will result in and to be set,
+ /// resulting in the
/// view being laid out and redrawn as appropriate in the next iteration of the .
///
///
@@ -277,7 +279,8 @@ public partial class View // Layout APIs
/// laid out (e.g. has been called).
///
///
- /// Changing this property will result in and to be set, resulting in the
+ /// Changing this property will result in and to be set,
+ /// resulting in the
/// view being laid out and redrawn as appropriate in the next iteration of the .
///
///
@@ -323,7 +326,8 @@ public partial class View // Layout APIs
/// laid out (e.g. has been called).
///
///
- /// Changing this property will result in and to be set, resulting in the
+ /// Changing this property will result in and to be set,
+ /// resulting in the
/// view being laid out and redrawn as appropriate in the next iteration of the .
///
///
@@ -354,14 +358,15 @@ public partial class View // Layout APIs
#region Core Layout API
///
- /// INTERNAL API - Performs layout of the specified views within the specified content size. Called by the Application main loop.
+ /// INTERNAL API - Performs layout of the specified views within the specified content size. Called by the Application
+ /// main loop.
///
/// The views to layout.
/// The size to bound the views by.
/// If any of the views needed to be laid out.
internal static bool Layout (IEnumerable views, Size contentSize)
{
- bool neededLayout = false;
+ var neededLayout = false;
foreach (View v in views)
{
@@ -404,7 +409,8 @@ public partial class View // Layout APIs
}
///
- /// Performs layout of the view and its subviews using the content size of either the or .
+ /// Performs layout of the view and its subviews using the content size of either the or
+ /// .
///
///
///
@@ -417,14 +423,12 @@ public partial class View // Layout APIs
///
///
/// If the view could not be laid out (typically because dependency was not ready).
- public bool Layout ()
- {
- return Layout (GetContainerSize ());
- }
+ public bool Layout () { return Layout (GetContainerSize ()); }
///
/// Sets the position and size of this view, relative to the SuperView's ContentSize (nominally the same as
- /// this.SuperView.GetContentSize ()) based on the values of , , ,
+ /// this.SuperView.GetContentSize ()) based on the values of , ,
+ /// ,
/// and .
///
///
@@ -467,6 +471,7 @@ public partial class View // Layout APIs
{
newW = _width.Calculate (0, superviewContentSize.Width, this, Dimension.Width);
newX = _x.Calculate (superviewContentSize.Width, newW, this, Dimension.Width);
+
if (newW != Frame.Width)
{
// Pos.Calculate gave us a new position. We need to redo dimension
@@ -483,6 +488,7 @@ public partial class View // Layout APIs
{
newH = _height.Calculate (0, superviewContentSize.Height, this, Dimension.Height);
newY = _y.Calculate (superviewContentSize.Height, newH, this, Dimension.Height);
+
if (newH != Frame.Height)
{
// Pos.Calculate gave us a new position. We need to redo dimension
@@ -494,7 +500,6 @@ public partial class View // Layout APIs
newY = _y.Calculate (superviewContentSize.Height, _height, this, Dimension.Height);
newH = _height.Calculate (newY, superviewContentSize.Height, this, Dimension.Height);
}
-
}
catch (LayoutException le)
{
@@ -552,7 +557,8 @@ public partial class View // Layout APIs
}
///
- /// INTERNAL API - Causes the view's subviews and adornments to be laid out within the view's content areas. Assumes the view's relative layout has been set via .
+ /// INTERNAL API - Causes the view's subviews and adornments to be laid out within the view's content areas. Assumes
+ /// the view's relative layout has been set via .
///
///
///
@@ -584,10 +590,12 @@ public partial class View // Layout APIs
{
Margin.LayoutSubviews ();
}
+
if (Border is { Subviews.Count: > 0 })
{
Border.LayoutSubviews ();
}
+
if (Padding is { Subviews.Count: > 0 })
{
Padding.LayoutSubviews ();
@@ -600,6 +608,7 @@ public partial class View // Layout APIs
List ordered = TopologicalSort (SuperView!, nodes, edges);
List redo = new ();
+
foreach (View v in ordered)
{
if (!v.Layout (contentSize))
@@ -608,7 +617,7 @@ public partial class View // Layout APIs
}
}
- bool layoutStillNeeded = false;
+ var layoutStillNeeded = false;
if (redo.Count > 0)
{
@@ -633,7 +642,7 @@ public partial class View // Layout APIs
}
}
- _needsLayout = layoutStillNeeded;
+ NeedsLayout = layoutStillNeeded;
OnSubviewsLaidOut (new (contentSize));
SubviewsLaidOut?.Invoke (this, new (contentSize));
@@ -648,8 +657,10 @@ public partial class View // Layout APIs
///
protected virtual void OnSubviewLayout (LayoutEventArgs args) { }
- /// Raised by before any subviews
- /// have been laid out.
+ ///
+ /// Raised by before any subviews
+ /// have been laid out.
+ ///
///
/// Subscribe to this event to perform tasks when the layout is changing.
///
@@ -677,65 +688,72 @@ public partial class View // Layout APIs
#region NeedsLayout
// We expose no setter for this to ensure that the ONLY place it's changed is in SetNeedsLayout
- private bool _needsLayout = true;
///
/// Indicates the View's Frame or the layout of the View's subviews (including Adornments) have
- /// changed since the last time the View was laid out.
+ /// changed since the last time the View was laid out.
///
///
- /// Used to prevent from needlessly computing
- /// layout.
- ///
+ ///
+ /// Used to prevent from needlessly computing
+ /// layout.
+ ///
///
///
/// if layout is needed.
///
- public bool NeedsLayout => _needsLayout;
+ public bool NeedsLayout { get; private set; } = true;
///
- /// Sets to return , indicating this View and all of it's subviews (including adornments) need to be laid out in the next Application iteration.
+ /// Sets to return , indicating this View and all of it's subviews
+ /// (including adornments) need to be laid out in the next Application iteration.
///
///
///
- /// The will cause to be called on the next so there is normally no reason to call see .
+ /// The will cause to be called on the next
+ /// so there is normally no reason to call see .
///
///
-
public void SetNeedsLayout ()
{
- _needsLayout = true;
+ NeedsLayout = true;
if (Margin is { Subviews.Count: > 0 })
{
Margin.SetNeedsLayout ();
}
+
if (Border is { Subviews.Count: > 0 })
{
Border.SetNeedsLayout ();
}
+
if (Padding is { Subviews.Count: > 0 })
{
Padding.SetNeedsLayout ();
}
// Use a stack to avoid recursion
- Stack stack = new Stack (Subviews);
+ Stack stack = new (Subviews);
while (stack.Count > 0)
{
View current = stack.Pop ();
+
if (!current.NeedsLayout)
{
- current._needsLayout = true;
+ current.NeedsLayout = true;
+
if (current.Margin is { Subviews.Count: > 0 })
{
current.Margin.SetNeedsLayout ();
}
+
if (current.Border is { Subviews.Count: > 0 })
{
current.Border.SetNeedsLayout ();
}
+
if (current.Padding is { Subviews.Count: > 0 })
{
current.Padding.SetNeedsLayout ();
@@ -757,7 +775,7 @@ public partial class View // Layout APIs
if (SuperView is null)
{
- foreach (var tl in Application.TopLevels)
+ foreach (Toplevel tl in Application.TopLevels)
{
tl.SetNeedsDraw ();
}
@@ -778,7 +796,6 @@ public partial class View // Layout APIs
#region Topological Sort
-
///
/// INTERNAL API - Collects all views and their dependencies from a given starting view for layout purposes. Used by
/// to create an ordered list of views to layout.
@@ -802,7 +819,7 @@ public partial class View // Layout APIs
}
///
- /// INTERNAL API - Collects dimension (where Width or Height is `DimView`) dependencies for a given view.
+ /// INTERNAL API - Collects dimension (where Width or Height is `DimView`) dependencies for a given view.
///
/// The dimension (width or height) to collect dependencies for.
/// The view for which to collect dimension dependencies.
@@ -813,7 +830,7 @@ public partial class View // Layout APIs
///
internal void CollectDim (Dim? dim, View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges)
{
- if (dim!.Has (out DimView dv))
+ if (dim!.Has (out DimView dv))
{
if (dv.Target != this)
{
@@ -821,7 +838,7 @@ public partial class View // Layout APIs
}
}
- if (dim!.Has (out DimCombine dc))
+ if (dim!.Has (out DimCombine dc))
{
CollectDim (dc.Left, from, ref nNodes, ref nEdges);
CollectDim (dc.Right, from, ref nNodes, ref nEdges);
@@ -845,6 +862,7 @@ public partial class View // Layout APIs
{
case PosView pv:
Debug.Assert (pv.Target is { });
+
if (pv.Target != this)
{
nEdges.Add ((pv.Target!, from));
@@ -960,22 +978,21 @@ public partial class View // Layout APIs
#region Utilities
///
- /// INTERNAL API - Gets the size of the SuperView's content (nominally the same as
- /// the SuperView's ) or the screen size if there's no SuperView.
+ /// INTERNAL API - Gets the size of the SuperView's content (nominally the same as
+ /// the SuperView's ) or the screen size if there's no SuperView.
///
///
private Size GetContainerSize ()
{
// TODO: Get rid of refs to Top
- Size superViewContentSize = SuperView?.GetContentSize () ??
- (Application.Top is { } && Application.Top != this && Application.Top.IsInitialized
- ? Application.Top.GetContentSize ()
- : Application.Screen.Size);
+ Size superViewContentSize = SuperView?.GetContentSize ()
+ ?? (Application.Top is { } && Application.Top != this && Application.Top.IsInitialized
+ ? Application.Top.GetContentSize ()
+ : Application.Screen.Size);
return superViewContentSize;
}
-
// BUGBUG: This method interferes with Dialog/MessageBox default min/max size.
// TODO: Get rid of MenuBar coupling as part of https://github.com/gui-cs/Terminal.Gui/issues/2975
///
@@ -1107,11 +1124,8 @@ public partial class View // Layout APIs
#endregion Utilities
-
#region Diagnostics and Verification
-
-
// Diagnostics to highlight when X or Y is read before the view has been initialized
private Pos VerifyIsInitialized (Pos pos, string member)
{
@@ -1228,12 +1242,12 @@ public partial class View // Layout APIs
if (bad != null)
{
throw new LayoutException (
- $"{view.GetType ().Name}.{name} = {bad.GetType ().Name} "
- + $"which depends on the SuperView's dimensions and the SuperView uses Dim.Auto."
- );
+ $"{view.GetType ().Name}.{name} = {bad.GetType ().Name} "
+ + $"which depends on the SuperView's dimensions and the SuperView uses Dim.Auto."
+ );
}
}
}
- #endregion Diagnostics and Verification
-}
\ No newline at end of file
+ #endregion Diagnostics and Verification
+}
diff --git a/Terminal.Gui/View/View.Mouse.cs b/Terminal.Gui/View/View.Mouse.cs
index 0afe506e5..4dbb65e38 100644
--- a/Terminal.Gui/View/View.Mouse.cs
+++ b/Terminal.Gui/View/View.Mouse.cs
@@ -666,15 +666,15 @@ public partial class View // Mouse APIs
if (start is not Adornment)
{
- if (start.Margin.Contains (currentLocation))
+ if (start.Margin is {} && start.Margin.Contains (currentLocation))
{
found = start.Margin;
}
- else if (start.Border.Contains (currentLocation))
+ else if (start.Border is {} && start.Border.Contains (currentLocation))
{
found = start.Border;
}
- else if (start.Padding.Contains (currentLocation))
+ else if (start.Padding is { } && start.Padding.Contains(currentLocation))
{
found = start.Padding;
}
diff --git a/Terminal.Gui/Views/Bar.cs b/Terminal.Gui/Views/Bar.cs
index 29c867a57..d986b5f64 100644
--- a/Terminal.Gui/Views/Bar.cs
+++ b/Terminal.Gui/Views/Bar.cs
@@ -87,8 +87,11 @@ public class Bar : View, IOrientation, IDesignable
///
public override void SetBorderStyle (LineStyle value)
{
- // The default changes the thickness. We don't want that. We just set the style.
- Border.LineStyle = value;
+ if (Border is { })
+ {
+ // The default changes the thickness. We don't want that. We just set the style.
+ Border.LineStyle = value;
+ }
}
#region IOrientation members
diff --git a/Terminal.Gui/Views/Menuv2.cs b/Terminal.Gui/Views/Menuv2.cs
index 928c36cd6..609fb962c 100644
--- a/Terminal.Gui/Views/Menuv2.cs
+++ b/Terminal.Gui/Views/Menuv2.cs
@@ -46,6 +46,7 @@ public class Menuv2 : Bar
// Menuv2 arranges the items horizontally.
// The first item has no left border, the last item has no right border.
// The Shortcuts are configured with the command, help, and key views aligned in reverse order (EndToStart).
+ ///
protected override void OnSubviewLayout (LayoutEventArgs args)
{
for (int index = 0; index < Subviews.Count; index++)
diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs
index 0eaefccf1..b139fcdd1 100644
--- a/Terminal.Gui/Views/Shortcut.cs
+++ b/Terminal.Gui/Views/Shortcut.cs
@@ -98,7 +98,11 @@ public class Shortcut : View, IOrientation, IDesignable
CanFocus = true;
SuperViewRendersLineCanvas = true;
- Border.Settings &= ~BorderSettings.Title;
+
+ if (Border is { })
+ {
+ Border.Settings &= ~BorderSettings.Title;
+ }
Width = GetWidthDimAuto ();
Height = Dim.Auto (DimAutoStyle.Content, 1);
@@ -237,39 +241,39 @@ public class Shortcut : View, IOrientation, IDesignable
ShowHide ();
ForceCalculateNaturalWidth ();
- if (Width is DimAuto widthAuto)
+ if (Width is DimAuto widthAuto || HelpView!.Margin is null)
{
+ return;
+ }
- }
+ // Frame.Width is smaller than the natural width. Reduce width of HelpView.
+ _maxHelpWidth = int.Max (0, GetContentSize ().Width - CommandView.Frame.Width - KeyView.Frame.Width);
+
+ if (_maxHelpWidth < 3)
+ {
+ Thickness t = GetMarginThickness ();
+
+ switch (_maxHelpWidth)
+ {
+ case 0:
+ case 1:
+ // Scrunch it by removing both margins
+ HelpView.Margin.Thickness = new (t.Right - 1, t.Top, t.Left - 1, t.Bottom);
+
+ break;
+
+ case 2:
+
+ // Scrunch just the right margin
+ HelpView.Margin.Thickness = new (t.Right, t.Top, t.Left - 1, t.Bottom);
+
+ break;
+ }
+ }
else
{
- // Frame.Width is smaller than the natural width. Reduce width of HelpView.
- _maxHelpWidth = int.Max (0, GetContentSize ().Width - CommandView.Frame.Width - KeyView.Frame.Width);
- if (_maxHelpWidth < 3)
- {
- Thickness t = GetMarginThickness ();
- switch (_maxHelpWidth)
- {
- case 0:
- case 1:
- // Scrunch it by removing both margins
- HelpView.Margin.Thickness = new (t.Right - 1, t.Top, t.Left - 1, t.Bottom);
-
- break;
-
- case 2:
-
- // Scrunch just the right margin
- HelpView.Margin.Thickness = new (t.Right, t.Top, t.Left - 1, t.Bottom);
-
- break;
- }
- }
- else
- {
- // Reset to default
- HelpView.Margin.Thickness = GetMarginThickness ();
- }
+ // Reset to default
+ HelpView.Margin.Thickness = GetMarginThickness ();
}
}
@@ -522,7 +526,11 @@ public class Shortcut : View, IOrientation, IDesignable
private void SetHelpViewDefaultLayout ()
{
- HelpView.Margin.Thickness = GetMarginThickness ();
+ if (HelpView.Margin is { })
+ {
+ HelpView.Margin.Thickness = GetMarginThickness ();
+ }
+
HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
_maxHelpWidth = HelpView.Text.GetColumns ();
HelpView.Width = Dim.Auto (DimAutoStyle.Text, maximumContentDim: Dim.Func ((() => _maxHelpWidth)));
@@ -654,7 +662,11 @@ public class Shortcut : View, IOrientation, IDesignable
private void SetKeyViewDefaultLayout ()
{
- KeyView.Margin.Thickness = GetMarginThickness ();
+ if (KeyView.Margin is { })
+ {
+ KeyView.Margin.Thickness = GetMarginThickness ();
+ }
+
KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
KeyView.Width = Dim.Auto (DimAutoStyle.Text, minimumContentDim: Dim.Func (() => MinimumKeyTextSize));
KeyView.Height = Dim.Fill ();
@@ -763,7 +775,10 @@ public class Shortcut : View, IOrientation, IDesignable
KeyView.ColorScheme = cs;
}
- CommandView.Margin.ColorScheme = base.ColorScheme;
+ if (CommandView.Margin is { })
+ {
+ CommandView.Margin.ColorScheme = base.ColorScheme;
+ }
}
///
diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs
index 3d77b9b9b..2b6007d61 100644
--- a/Terminal.Gui/Views/TabView.cs
+++ b/Terminal.Gui/Views/TabView.cs
@@ -216,7 +216,7 @@ public class TabView : View
///
/// Updates the control to use the latest state settings in . This can change the size of the
/// client area of the tab (for rendering the selected tab's content). This method includes a call to
- /// .
+ /// .
///
public void ApplyStyleChanges ()
{
@@ -288,7 +288,7 @@ public class TabView : View
/// Updates to be a valid index of .
/// The value to validate.
- /// Changes will not be immediately visible in the display until you call .
+ /// Changes will not be immediately visible in the display until you call .
/// The valid for the given value.
public int EnsureValidScrollOffsets (int value) { return Math.Max (Math.Min (value, Tabs.Count - 1), 0); }
diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs
index 1a183bf6c..7b19eb0a5 100644
--- a/Terminal.Gui/Views/TableView/TableView.cs
+++ b/Terminal.Gui/Views/TableView/TableView.cs
@@ -583,7 +583,7 @@ public class TableView : View, IDesignable
/// not been set.
///
///
- /// Changes will not be immediately visible in the display until you call
+ /// Changes will not be immediately visible in the display until you call
///
public void EnsureSelectedCellIsVisible ()
{
@@ -644,7 +644,7 @@ public class TableView : View, IDesignable
/// (by adjusting them to the nearest existing cell). Has no effect if has not been set.
///
///
- /// Changes will not be immediately visible in the display until you call
+ /// Changes will not be immediately visible in the display until you call
///
public void EnsureValidScrollOffsets ()
{
@@ -663,7 +663,7 @@ public class TableView : View, IDesignable
/// has not been set.
///
///
- /// Changes will not be immediately visible in the display until you call
+ /// Changes will not be immediately visible in the display until you call
///
public void EnsureValidSelection ()
{
@@ -1227,7 +1227,7 @@ public class TableView : View, IDesignable
/// Updates the view to reflect changes to and to ( /
/// ) etc
///
- /// This always calls
+ /// This always calls
public void Update ()
{
if (!IsInitialized || TableIsNullOrInvisible ())