diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 000000000..79faf752f
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,5 @@
+
+
+ $(DefineConstants);DIMAUTO
+
+
\ No newline at end of file
diff --git a/Terminal.Gui/Directory.Build.props b/Terminal.Gui/Directory.Build.props
index 6bf2f2e2c..f22179be1 100644
--- a/Terminal.Gui/Directory.Build.props
+++ b/Terminal.Gui/Directory.Build.props
@@ -7,5 +7,4 @@
-->
Miguel de Icaza, Charlie Kindel (@tig), @BDisp
-
\ No newline at end of file
diff --git a/Terminal.Gui/View/Layout/PosDim.cs b/Terminal.Gui/View/Layout/PosDim.cs
index 6da3a0a03..42ff8d8d4 100644
--- a/Terminal.Gui/View/Layout/PosDim.cs
+++ b/Terminal.Gui/View/Layout/PosDim.cs
@@ -1,4 +1,4 @@
-namespace Terminal.Gui;
+namespace Terminal.Gui;
///
/// Describes the position of a which can be an absolute value, a percentage, centered, or
@@ -43,7 +43,7 @@
///
///
///
-///
+///
///
///
/// Creates a object that is anchored to the end (right side or bottom) of
@@ -132,8 +132,7 @@ public class Pos
/// The view will be shifted left or up by the amount specified.
///
/// This sample shows how align a to the bottom-right of a .
- ///
- /// // See Issue #502
+ ///
/// anchorButton.X = Pos.AnchorEnd () - (Pos.Right (anchorButton) - Pos.Left (anchorButton));
/// anchorButton.Y = Pos.AnchorEnd (1);
///
@@ -159,19 +158,19 @@ public class Pos
///
/// The that depends on the other view.
/// The that will be tracked.
- public static Pos Bottom (View view) { return new PosView (view, 3); }
+ public static Pos Bottom (View view) { return new PosView (view, Side.Bottom); }
/// Creates a object that can be used to center the .
/// The center Pos.
///
- /// This creates a that is centered horizontally, is 50% of the way down, is 30% the height, and
+ /// This creates a centered horizontally, is 50% of the way down, is 30% the height, and
/// is 80% the width of the it added to.
- ///
+ ///
/// var textView = new TextView () {
- /// X = Pos.Center (),
- /// Y = Pos.Percent (50),
- /// Width = Dim.Percent (80),
- /// Height = Dim.Percent (30),
+ /// X = Pos.Center (),
+ /// Y = Pos.Percent (50),
+ /// Width = Dim.Percent (80),
+ /// Height = Dim.Percent (30),
/// };
///
///
@@ -200,7 +199,7 @@ public class Pos
/// Creates a object that tracks the Left (X) position of the specified .
/// The that depends on the other view.
/// The that will be tracked.
- public static Pos Left (View view) { return new PosView (view, 0); }
+ public static Pos Left (View view) { return new PosView (view, Side.X); }
/// Adds a to a , yielding a new .
/// The first to add.
@@ -246,27 +245,27 @@ public class Pos
/// Creates a percentage object
/// The percent object.
- /// A value between 0 and 100 representing the percentage.
+ /// A value between 0 and 100 representing the percentage.
///
- /// This creates a that is centered horizontally, is 50% of the way down, is 30% the height, and
+ /// This creates a centered horizontally, is 50% of the way down, is 30% the height, and
/// is 80% the width of the it added to.
- ///
- /// var textView = new TextView () {
- /// X = Pos.Center (),
- /// Y = Pos.Percent (50),
- /// Width = Dim.Percent (80),
- /// Height = Dim.Percent (30),
+ ///
+ /// var textView = new TextField {
+ /// X = Pos.Center (),
+ /// Y = Pos.Percent (50),
+ /// Width = Dim.Percent (80),
+ /// Height = Dim.Percent (30),
/// };
///
///
- public static Pos Percent (float n)
+ public static Pos Percent (float percent)
{
- if (n is < 0 or > 100)
+ if (percent is < 0 or > 100)
{
- throw new ArgumentException ("Percent value must be between 0 and 100");
+ throw new ArgumentException ("Percent value must be between 0 and 100.");
}
- return new PosFactor (n / 100);
+ return new PosFactor (percent / 100);
}
///
@@ -275,49 +274,46 @@ public class Pos
///
/// The that depends on the other view.
/// The that will be tracked.
- public static Pos Right (View view) { return new PosView (view, 2); }
+ public static Pos Right (View view) { return new PosView (view, Side.Right); }
/// Creates a object that tracks the Top (Y) position of the specified .
/// The that depends on the other view.
/// The that will be tracked.
- public static Pos Top (View view) { return new PosView (view, 1); }
+ public static Pos Top (View view) { return new PosView (view, Side.Y); }
/// Creates a object that tracks the Left (X) position of the specified .
/// The that depends on the other view.
/// The that will be tracked.
- public static Pos X (View view) { return new PosView (view, 0); }
+ public static Pos X (View view) { return new PosView (view, Side.X); }
/// Creates a object that tracks the Top (Y) position of the specified .
/// The that depends on the other view.
/// The that will be tracked.
- public static Pos Y (View view) { return new PosView (view, 1); }
+ public static Pos Y (View view) { return new PosView (view, Side.Y); }
internal virtual int Anchor (int width) { return 0; }
+ // BUGBUG: newPos is never used
private static void SetPosCombine (Pos left, PosCombine newPos)
{
- var view = left as PosView;
-
- if (view is { })
+ if (left is PosView view)
{
view.Target.SetNeedsLayout ();
}
}
- internal class PosAbsolute : Pos
+ internal class PosAbsolute (int n) : Pos
{
- private readonly int _n;
- public PosAbsolute (int n) { _n = n; }
+ private readonly int _n = n;
public override bool Equals (object other) { return other is PosAbsolute abs && abs._n == _n; }
public override int GetHashCode () { return _n.GetHashCode (); }
public override string ToString () { return $"Absolute({_n})"; }
internal override int Anchor (int width) { return _n; }
}
- internal class PosAnchorEnd : Pos
+ internal class PosAnchorEnd (int offset) : Pos
{
- private readonly int _offset;
- public PosAnchorEnd (int offset) { _offset = offset; }
+ private readonly int _offset = offset;
public override bool Equals (object other) { return other is PosAnchorEnd anchorEnd && anchorEnd._offset == _offset; }
public override int GetHashCode () { return _offset.GetHashCode (); }
public override string ToString () { return $"AnchorEnd({_offset})"; }
@@ -330,17 +326,10 @@ public class Pos
internal override int Anchor (int width) { return width / 2; }
}
- internal class PosCombine : Pos
+ internal class PosCombine (bool add, Pos left, Pos right) : Pos
{
- internal bool _add;
- internal Pos _left, _right;
-
- public PosCombine (bool add, Pos left, Pos right)
- {
- _left = left;
- _right = right;
- _add = add;
- }
+ internal bool _add = add;
+ internal Pos _left = left, _right = right;
public override string ToString () { return $"Combine({_left}{(_add ? '+' : '-')}{_right})"; }
@@ -358,10 +347,9 @@ public class Pos
}
}
- internal class PosFactor : Pos
+ internal class PosFactor (float factor) : Pos
{
- private readonly float _factor;
- public PosFactor (float n) { _factor = n; }
+ private readonly float _factor = factor;
public override bool Equals (object other) { return other is PosFactor f && f._factor == _factor; }
public override int GetHashCode () { return _factor.GetHashCode (); }
public override string ToString () { return $"Factor({_factor})"; }
@@ -369,75 +357,57 @@ public class Pos
}
// Helper class to provide dynamic value by the execution of a function that returns an integer.
- internal class PosFunc : Pos
+ internal class PosFunc (Func n) : Pos
{
- private readonly Func _function;
- public PosFunc (Func n) { _function = n; }
+ private readonly Func _function = n;
public override bool Equals (object other) { return other is PosFunc f && f._function () == _function (); }
public override int GetHashCode () { return _function.GetHashCode (); }
public override string ToString () { return $"PosFunc({_function ()})"; }
internal override int Anchor (int width) { return _function (); }
}
- internal class PosView : Pos
+ internal enum Side
{
- public readonly View Target;
+ X = 0,
+ Y = 1,
+ Right = 2,
+ Bottom = 3,
+ }
- private readonly int side;
-
- public PosView (View view, int side)
- {
- Target = view;
- this.side = side;
- }
+ internal class PosView (View view, Side side) : Pos
+ {
+ public readonly View Target = view;
public override bool Equals (object other) { return other is PosView abs && abs.Target == Target; }
public override int GetHashCode () { return Target.GetHashCode (); }
public override string ToString ()
{
- string tside;
-
- switch (side)
- {
- case 0:
- tside = "x";
-
- break;
- case 1:
- tside = "y";
-
- break;
- case 2:
- tside = "right";
-
- break;
- case 3:
- tside = "bottom";
-
- break;
- default:
- tside = "unknown";
-
- break;
- }
+ string side1 = side switch
+ {
+ Side.X => "x",
+ Side.Y => "y",
+ Side.Right => "right",
+ Side.Bottom => "bottom",
+ _ => "unknown"
+ };
if (Target is null)
{
throw new NullReferenceException (nameof (Target));
}
- return $"View(side={tside},target={Target})";
+ return $"View(side={side1},target={Target})";
}
internal override int Anchor (int width)
{
switch (side)
{
- case 0: return Target.Frame.X;
- case 1: return Target.Frame.Y;
- case 2: return Target.Frame.Right;
- case 3: return Target.Frame.Bottom;
+ case Side.X: return Target.Frame.X;
+ case Side.Y: return Target.Frame.Y;
+ case Side.Right: return Target.Frame.Right;
+ case Side.Bottom: return Target.Frame.Bottom;
default:
return 0;
}
@@ -465,6 +435,15 @@ public class Pos
///
///
///
+///
+///
+///
+/// Creates a object that automatically sizes the view to fit
+/// the view's SubViews.
+///
+///
+///
+///
///
///
///
@@ -486,8 +465,8 @@ public class Pos
///
///
///
-/// Creates a object that fills the dimension, leaving the specified number
-/// of columns for a margin.
+/// Creates a object that fills the dimension from the View's X position
+/// to the end of the super view's width, leaving the specified number of columns for a margin.
///
///
///
@@ -514,6 +493,64 @@ public class Pos
///
public class Dim
{
+ ///
+ /// Specifies how will compute the dimension.
+ ///
+ public enum DimAutoStyle
+ {
+ ///
+ /// The dimension will be computed using both the view's and
+ /// .
+ /// The larger of the corresponding text dimension or Subview in
+ /// with the largest corresponding position plus dimension will determine the dimension.
+ ///
+ Auto,
+
+ ///
+ /// The Subview in with the largest corresponding position plus dimension
+ /// will determine the dimension.
+ /// The corresponding dimension of the view's will be ignored.
+ ///
+ Subviews,
+
+ ///
+ /// The corresponding dimension of the view's , formatted using the
+ /// settings,
+ /// will be used to determine the dimension.
+ /// The corresponding dimensions of the will be ignored.
+ ///
+ Text
+ }
+
+ ///
+ /// Creates a object that automatically sizes the view to fit all of the view's SubViews and/or Text.
+ ///
+ ///
+ /// This initializes a with two SubViews. The view will be automatically sized to fit the two
+ /// SubViews.
+ ///
+ /// var button = new Button () { Text = "Click Me!", X = 1, Y = 1, Width = 10, Height = 1 };
+ /// var textField = new TextField { Text = "Type here", X = 1, Y = 2, Width = 20, Height = 1 };
+ /// var view = new Window () { Title = "MyWindow", X = 0, Y = 0, Width = Dim.AutoSize (), Height = Dim.AutoSize () };
+ /// view.Add (button, textField);
+ ///
+ ///
+ /// The AutoSize object.
+ ///
+ /// Specifies how will compute the dimension. The default is .
+ ///
+ /// Specifies the minimum dimension that view will be automatically sized to.
+ /// Specifies the maximum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED.
+ public static Dim Auto (DimAutoStyle style = DimAutoStyle.Auto, Dim min = null, Dim max = null)
+ {
+ if (max != null)
+ {
+ throw new NotImplementedException (@"max is not implemented");
+ }
+
+ return new DimAuto (style, min, max);
+ }
+
/// Determines whether the specified object is equal to the current object.
/// The object to compare with the current object.
///
@@ -545,11 +582,11 @@ public class Dim
/// Creates a object that tracks the Height of the specified .
/// The height of the other .
/// The view that will be tracked.
- public static Dim Height (View view) { return new DimView (view, 0); }
+ public static Dim Height (View view) { return new DimView (view, Side.Height); }
- /// Adds a to a , yielding a new .
- /// The first to add.
- /// The second to add.
+ /// Adds a to a , yielding a new .
+ /// The first to add.
+ /// The second to add.
/// The that is the sum of the values of left and right.
public static Dim operator + (Dim left, Dim right)
{
@@ -570,11 +607,11 @@ public class Dim
public static implicit operator Dim (int n) { return new DimAbsolute (n); }
///
- /// Subtracts a from a , yielding a new
+ /// Subtracts a from a , yielding a new
/// .
///
- /// The to subtract from (the minuend).
- /// The to subtract (the subtrahend).
+ /// The to subtract from (the minuend).
+ /// The to subtract (the subtrahend).
/// The that is the left minus right.
public static Dim operator - (Dim left, Dim right)
{
@@ -591,31 +628,31 @@ public class Dim
/// Creates a percentage object that is a percentage of the width or height of the SuperView.
/// The percent object.
- /// A value between 0 and 100 representing the percentage.
- ///
- /// If true the Percent is computed based on the remaining space after the X/Y anchor positions. If
- /// false is computed based on the whole original space.
+ /// A value between 0 and 100 representing the percentage.
+ ///
+ /// If the dimension is computed using the View's position ( or ).
+ /// If the dimension is computed using the View's .
///
///
- /// This initializes a that is centered horizontally, is 50% of the way down, is 30% the height,
- /// and is 80% the width of the it added to.
- ///
- /// var textView = new TextView () {
- /// X = Pos.Center (),
- /// Y = Pos.Percent (50),
- /// Width = Dim.Percent (80),
- /// Height = Dim.Percent (30),
+ /// This initializes a that will be centered horizontally, is 50% of the way down, is 30% the height,
+ /// and is 80% the width of the SuperView.
+ ///
+ /// var textView = new TextField {
+ /// X = Pos.Center (),
+ /// Y = Pos.Percent (50),
+ /// Width = Dim.Percent (80),
+ /// Height = Dim.Percent (30),
/// };
///
///
- public static Dim Percent (float n, bool r = false)
+ public static Dim Percent (float percent, bool usePosition = false)
{
- if (n is < 0 or > 100)
+ if (percent is < 0 or > 100)
{
throw new ArgumentException ("Percent value must be between 0 and 100");
}
- return new DimFactor (n / 100, r);
+ return new DimFactor (percent / 100, usePosition);
}
/// Creates an Absolute from the specified integer value.
@@ -626,34 +663,37 @@ public class Dim
/// Creates a object that tracks the Width of the specified .
/// The width of the other .
/// The view that will be tracked.
- public static Dim Width (View view) { return new DimView (view, 1); }
+ public static Dim Width (View view) { return new DimView (view, Side.Width); }
internal virtual int Anchor (int width) { return 0; }
// BUGBUG: newPos is never used.
private static void SetDimCombine (Dim left, DimCombine newPos) { (left as DimView)?.Target.SetNeedsLayout (); }
- internal class DimAbsolute : Dim
+ internal class DimAbsolute (int n) : Dim
{
- private readonly int _n;
- public DimAbsolute (int n) { _n = n; }
+ private readonly int _n = n;
public override bool Equals (object other) { return other is DimAbsolute abs && abs._n == _n; }
public override int GetHashCode () { return _n.GetHashCode (); }
public override string ToString () { return $"Absolute({_n})"; }
internal override int Anchor (int width) { return _n; }
}
- internal class DimCombine : Dim
+ internal class DimAuto (DimAutoStyle style, Dim min, Dim max) : Dim
{
- internal bool _add;
- internal Dim _left, _right;
+ internal readonly Dim _max = max;
+ internal readonly Dim _min = min;
+ internal readonly DimAutoStyle _style = style;
- public DimCombine (bool add, Dim left, Dim right)
- {
- _left = left;
- _right = right;
- _add = add;
- }
+ public override bool Equals (object other) { return other is DimAuto auto && auto._min == _min && auto._max == _max && auto._style == _style; }
+ public override int GetHashCode () { return HashCode.Combine (base.GetHashCode (), _min, _max, _style); }
+ public override string ToString () { return $"Auto({_style},{_min},{_max})"; }
+ }
+
+ internal class DimCombine (bool add, Dim left, Dim right) : Dim
+ {
+ internal bool _add = add;
+ internal Dim _left = left, _right = right;
public override string ToString () { return $"Combine({_left}{(_add ? '+' : '-')}{_right})"; }
@@ -671,16 +711,10 @@ public class Dim
}
}
- internal class DimFactor : Dim
+ internal class DimFactor (float n, bool usePosition = false) : Dim
{
- private readonly float _factor;
- private readonly bool _remaining;
-
- public DimFactor (float n, bool r = false)
- {
- _factor = n;
- _remaining = r;
- }
+ private readonly float _factor = n;
+ private readonly bool _remaining = usePosition;
public override bool Equals (object other) { return other is DimFactor f && f._factor == _factor && f._remaining == _remaining; }
public override int GetHashCode () { return _factor.GetHashCode (); }
@@ -689,10 +723,9 @@ public class Dim
internal override int Anchor (int width) { return (int)(width * _factor); }
}
- internal class DimFill : Dim
+ internal class DimFill (int margin) : Dim
{
- private readonly int _margin;
- public DimFill (int margin) { _margin = margin; }
+ private readonly int _margin = margin;
public override bool Equals (object other) { return other is DimFill fill && fill._margin == _margin; }
public override int GetHashCode () { return _margin.GetHashCode (); }
public override string ToString () { return $"Fill({_margin})"; }
@@ -700,21 +733,26 @@ public class Dim
}
// Helper class to provide dynamic value by the execution of a function that returns an integer.
- internal class DimFunc : Dim
+ internal class DimFunc (Func n) : Dim
{
- private readonly Func _function;
- public DimFunc (Func n) { _function = n; }
+ private readonly Func _function = n;
public override bool Equals (object other) { return other is DimFunc f && f._function () == _function (); }
public override int GetHashCode () { return _function.GetHashCode (); }
public override string ToString () { return $"DimFunc({_function ()})"; }
internal override int Anchor (int width) { return _function (); }
}
+ internal enum Side
+ {
+ Height = 0,
+ Width = 1
+ }
+
internal class DimView : Dim
{
- private readonly int _side;
+ private readonly Side _side;
- public DimView (View view, int side)
+ internal DimView (View view, Side side)
{
Target = view;
_side = side;
@@ -731,22 +769,22 @@ public class Dim
throw new NullReferenceException ();
}
- string tside = _side switch
+ string side = _side switch
{
- 0 => "Height",
- 1 => "Width",
+ Side.Height => "Height",
+ Side.Width => "Width",
_ => "unknown"
};
- return $"View({tside},{Target})";
+ return $"View({side},{Target})";
}
internal override int Anchor (int width)
{
return _side switch
{
- 0 => Target.Frame.Height,
- 1 => Target.Frame.Width,
+ Side.Height => Target.Frame.Height,
+ Side.Width => Target.Frame.Width,
_ => 0
};
}
diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs
index af1e417a9..17b8e9400 100644
--- a/Terminal.Gui/View/Layout/ViewLayout.cs
+++ b/Terminal.Gui/View/Layout/ViewLayout.cs
@@ -26,9 +26,12 @@ public enum LayoutStyle
///
/// Indicates one or more of the , , , or
- /// objects are relative to the and are computed at layout time.
- /// The position and size of the view will be computed based on these objects at layout time.
- /// will provide the absolute computed values.
+ ///
+ /// objects are relative to the and are computed at layout time. The position and size of
+ /// the
+ /// view
+ /// will be computed based on these objects at layout time. will provide the absolute computed
+ /// values.
///
Computed
}
@@ -102,7 +105,7 @@ public partial class View
}
Point boundsOffset = super.GetBoundsOffset ();
- boundsOffset.Offset(super.Frame.X, super.Frame.Y);
+ boundsOffset.Offset (super.Frame.X, super.Frame.Y);
ret.X += boundsOffset.X;
ret.Y += boundsOffset.Y;
super = super.SuperView;
@@ -440,6 +443,7 @@ public partial class View
}
}
+
/// If is true, resizes the view.
///
///
@@ -451,7 +455,7 @@ public partial class View
}
var boundsChanged = true;
- Size newFrameSize = GetAutoSize ();
+ Size newFrameSize = GetTextAutoSize ();
if (IsInitialized && newFrameSize != Frame.Size)
{
@@ -469,141 +473,6 @@ public partial class View
return boundsChanged;
}
-
- /// Determines if the View's can be set to a new value.
- /// TrySetHeight can only be called when AutoSize is true (or being set to true).
- ///
- ///
- /// Contains the width that would result if were set to
- /// "/>
- ///
- ///
- /// if the View's can be changed to the specified value. False
- /// otherwise.
- ///
- internal bool TrySetHeight (int desiredHeight, out int resultHeight)
- {
- int h = desiredHeight;
- bool canSetHeight;
-
- switch (Height)
- {
- case Dim.DimCombine _:
- case Dim.DimView _:
- case Dim.DimFill _:
- // It's a Dim.DimCombine and so can't be assigned. Let it have it's height anchored.
- h = Height.Anchor (h);
- canSetHeight = !ValidatePosDim;
-
- break;
- case Dim.DimFactor factor:
- // Tries to get the SuperView height otherwise the view height.
- int sh = SuperView is { } ? SuperView.Frame.Height : h;
-
- if (factor.IsFromRemaining ())
- {
- sh -= Frame.Y;
- }
-
- h = Height.Anchor (sh);
- canSetHeight = !ValidatePosDim;
-
- break;
- default:
- canSetHeight = true;
-
- break;
- }
-
- resultHeight = h;
-
- return canSetHeight;
- }
-
- /// Determines if the View's can be set to a new value.
- /// TrySetWidth can only be called when AutoSize is true (or being set to true).
- ///
- ///
- /// Contains the width that would result if were set to
- /// "/>
- ///
- ///
- /// if the View's can be changed to the specified value. False
- /// otherwise.
- ///
- internal bool TrySetWidth (int desiredWidth, out int resultWidth)
- {
- int w = desiredWidth;
- bool canSetWidth;
-
- switch (Width)
- {
- case Dim.DimCombine _:
- case Dim.DimView _:
- case Dim.DimFill _:
- // It's a Dim.DimCombine and so can't be assigned. Let it have it's Width anchored.
- w = Width.Anchor (w);
- canSetWidth = !ValidatePosDim;
-
- break;
- case Dim.DimFactor factor:
- // Tries to get the SuperView Width otherwise the view Width.
- int sw = SuperView is { } ? SuperView.Frame.Width : w;
-
- if (factor.IsFromRemaining ())
- {
- sw -= Frame.X;
- }
-
- w = Width.Anchor (sw);
- canSetWidth = !ValidatePosDim;
-
- break;
- default:
- canSetWidth = true;
-
- break;
- }
-
- resultWidth = w;
-
- return canSetWidth;
- }
-
- /// Resizes the View to fit the specified size. Factors in the HotKey.
- /// ResizeBoundsToFit can only be called when AutoSize is true (or being set to true).
- ///
- /// whether the Bounds was changed or not
- private bool ResizeBoundsToFit (Size size)
- {
- //if (AutoSize == false) {
- // throw new InvalidOperationException ("ResizeBoundsToFit can only be called when AutoSize is true");
- //}
-
- var boundsChanged = false;
- bool canSizeW = TrySetWidth (size.Width - GetHotKeySpecifierLength (), out int rW);
- bool canSizeH = TrySetHeight (size.Height - GetHotKeySpecifierLength (false), out int rH);
-
- if (canSizeW)
- {
- boundsChanged = true;
- _width = rW;
- }
-
- if (canSizeH)
- {
- boundsChanged = true;
- _height = rH;
- }
-
- if (boundsChanged)
- {
- Bounds = new (Bounds.X, Bounds.Y, canSizeW ? rW : Bounds.Width, canSizeH ? rH : Bounds.Height);
- }
-
- return boundsChanged;
- }
-
#endregion AutoSize
#region Layout Engine
@@ -886,8 +755,8 @@ public partial class View
public event EventHandler LayoutStarted;
///
- /// Invoked when a view starts executing or when the dimensions of the view have changed, for example in response
- /// to the container view or terminal resizing.
+ /// Invoked when a view starts executing or when the dimensions of the view have changed, for example in response to
+ /// the container view or terminal resizing.
///
///
///
@@ -900,9 +769,7 @@ public partial class View
{
if (!IsInitialized)
{
- Debug.WriteLine (
- $"WARNING: LayoutSubviews called before view has been initialized. This is likely a bug in {this}"
- );
+ Debug.WriteLine ($"WARNING: LayoutSubviews called before view has been initialized. This is likely a bug in {this}");
}
if (!LayoutNeeded)
@@ -910,6 +777,8 @@ public partial class View
return;
}
+ CheckDimAuto ();
+
LayoutAdornments ();
Rectangle oldBounds = Bounds;
@@ -925,7 +794,24 @@ public partial class View
foreach (View v in ordered)
{
- LayoutSubview (v, new (GetBoundsOffset (), Bounds.Size));
+ if (v.Width is Dim.DimAuto || v.Height is Dim.DimAuto)
+ {
+ // If the view is auto-sized...
+ Rectangle f = v.Frame;
+ v._frame = new (v.Frame.X, v.Frame.Y, 0, 0);
+ LayoutSubview (v, new (GetBoundsOffset (), Bounds.Size));
+
+ if (v.Frame != f)
+ {
+ // The subviews changed; do it again
+ v.LayoutNeeded = true;
+ LayoutSubview (v, new (GetBoundsOffset (), Bounds.Size));
+ }
+ }
+ else
+ {
+ LayoutSubview (v, new (GetBoundsOffset (), Bounds.Size));
+ }
}
// If the 'to' is rooted to 'from' and the layoutstyle is Computed it's a special-case.
@@ -1032,6 +918,8 @@ public partial class View
Debug.Assert (_width is { });
Debug.Assert (_height is { });
+ CheckDimAuto ();
+
int newX, newW, newY, newH;
var autosize = Size.Empty;
@@ -1039,7 +927,7 @@ public partial class View
{
// 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 ();
+ autosize = GetTextAutoSize ();
}
// TODO: Since GetNewLocationAndDimension does not depend on View, it can be moved into PosDim.cs
@@ -1094,6 +982,41 @@ public partial class View
break;
+ case Dim.DimAuto auto:
+ Thickness thickness = GetAdornmentsThickness ();
+ var text = 0;
+ var subviews = 0;
+
+ int superviewSize = width ? superviewBounds.Width : superviewBounds.Height;
+ int autoMin = auto._min?.Anchor (superviewSize) ?? 0;
+
+ if (superviewSize < autoMin)
+ {
+ Debug.WriteLine ($"WARNING: DimAuto specifies a min size ({autoMin}), but the SuperView's bounds are smaller ({superviewSize}).");
+ }
+
+ if (auto._style is Dim.DimAutoStyle.Text or Dim.DimAutoStyle.Auto)
+ {
+ text = int.Max (width ? TextFormatter.Size.Width : TextFormatter.Size.Height, autoMin);
+ }
+
+ if (auto._style is Dim.DimAutoStyle.Subviews or Dim.DimAutoStyle.Auto)
+ {
+ subviews = Subviews.Count == 0
+ ? 0
+ : Subviews
+ .Where (v => width ? v.X is not Pos.PosAnchorEnd : v.Y is not Pos.PosAnchorEnd)
+ .Max (v => width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height);
+ }
+
+ int max = int.Max (text, subviews);
+
+ newDimension = int.Max (width ? max + thickness.Left + thickness.Right : max + thickness.Top + thickness.Bottom, autoMin);
+ // If _max is set, use it. Otherwise, use superview;'s size to constrain
+ newDimension = int.Min (newDimension, auto._max?.Anchor (superviewSize) ?? superviewSize);
+
+ break;
+
case Dim.DimAbsolute:
// DimAbsolute.Anchor (int width) ignores width and returns n
newDimension = Math.Max (d.Anchor (0), 0);
@@ -1104,6 +1027,12 @@ public partial class View
break;
case Dim.DimFill:
+ // Fills the remaining space.
+ newDimension = Math.Max (d.Anchor (dimension - location), 0 /* width ? superviewBounds.Width : superviewBounds.Height*/);
+ newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
+
+ break;
+
default:
newDimension = Math.Max (d.Anchor (dimension - location), 0);
newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
@@ -1413,6 +1342,174 @@ public partial class View
return result;
} // TopologicalSort
+ /// Determines if the View's can be set to a new value.
+ /// TrySetHeight can only be called when AutoSize is true (or being set to true).
+ ///
+ ///
+ /// Contains the width that would result if were set to
+ /// "/>
+ ///
+ ///
+ /// if the View's can be changed to the specified value. False
+ /// otherwise.
+ ///
+ internal bool TrySetHeight (int desiredHeight, out int resultHeight)
+ {
+ int h = desiredHeight;
+ bool canSetHeight;
+
+ switch (Height)
+ {
+ case Dim.DimCombine _:
+ case Dim.DimView _:
+ case Dim.DimFill _:
+ // It's a Dim.DimCombine and so can't be assigned. Let it have it's height anchored.
+ h = Height.Anchor (h);
+ canSetHeight = !ValidatePosDim;
+
+ break;
+ case Dim.DimFactor factor:
+ // Tries to get the SuperView height otherwise the view height.
+ int sh = SuperView != null ? SuperView.Frame.Height : h;
+
+ if (factor.IsFromRemaining ())
+ {
+ sh -= Frame.Y;
+ }
+
+ h = Height.Anchor (sh);
+ canSetHeight = !ValidatePosDim;
+
+ break;
+ default:
+ canSetHeight = true;
+
+ break;
+ }
+
+ resultHeight = h;
+
+ return canSetHeight;
+ }
+
+ /// Determines if the View's can be set to a new value.
+ /// TrySetWidth can only be called when AutoSize is true (or being set to true).
+ ///
+ ///
+ /// Contains the width that would result if were set to
+ /// "/>
+ ///
+ ///
+ /// if the View's can be changed to the specified value. False
+ /// otherwise.
+ ///
+ internal bool TrySetWidth (int desiredWidth, out int resultWidth)
+ {
+ int w = desiredWidth;
+ bool canSetWidth;
+
+ switch (Width)
+ {
+ case Dim.DimCombine _:
+ case Dim.DimView _:
+ case Dim.DimFill _:
+ // It's a Dim.DimCombine and so can't be assigned. Let it have it's Width anchored.
+ w = Width.Anchor (w);
+ canSetWidth = !ValidatePosDim;
+
+ break;
+ case Dim.DimFactor factor:
+ // Tries to get the SuperView Width otherwise the view Width.
+ int sw = SuperView != null ? SuperView.Frame.Width : w;
+
+ if (factor.IsFromRemaining ())
+ {
+ sw -= Frame.X;
+ }
+
+ w = Width.Anchor (sw);
+ canSetWidth = !ValidatePosDim;
+
+ break;
+ default:
+ canSetWidth = true;
+
+ break;
+ }
+
+ resultWidth = w;
+
+ return canSetWidth;
+ }
+
+ ///
+ /// Throws an if any SubViews are using Dim objects that depend on this
+ /// Views dimensions.
+ ///
+ ///
+ private void CheckDimAuto ()
+ {
+ if (!ValidatePosDim || !IsInitialized || (Width is not Dim.DimAuto && Height is not Dim.DimAuto))
+ {
+ return;
+ }
+
+ void ThrowInvalid (View view, object checkPosDim, string name)
+ {
+ // TODO: Figure out how to make CheckDimAuto deal with PosCombine
+ object bad = null;
+
+ switch (checkPosDim)
+ {
+ case Pos pos and not Pos.PosAbsolute and not Pos.PosView and not Pos.PosCombine:
+ bad = pos;
+
+ break;
+
+ case Pos pos and Pos.PosCombine:
+ // Recursively check for not Absolute or not View
+ ThrowInvalid (view, (pos as Pos.PosCombine)._left, name);
+ ThrowInvalid (view, (pos as Pos.PosCombine)._right, name);
+
+ break;
+
+ case Dim dim and not Dim.DimAbsolute and not Dim.DimView and not Dim.DimCombine:
+ bad = dim;
+
+ break;
+
+ case Dim dim and Dim.DimCombine:
+ // Recursively check for not Absolute or not View
+ ThrowInvalid (view, (dim as Dim.DimCombine)._left, name);
+ ThrowInvalid (view, (dim as Dim.DimCombine)._right, name);
+
+ break;
+ }
+
+ if (bad != null)
+ {
+ throw new InvalidOperationException (
+ @$"{view.GetType ().Name}.{name} = {bad.GetType ().Name} which depends on the SuperView's dimensions and the SuperView uses Dim.Auto.");
+ }
+ }
+
+ // Verify none of the subviews are using Dim objects that depend on the SuperView's dimensions.
+ foreach (View view in Subviews)
+ {
+ if (Width is Dim.DimAuto { _min: null })
+ {
+ ThrowInvalid (view, view.Width, nameof (view.Width));
+ ThrowInvalid (view, view.X, nameof (view.X));
+ }
+
+ if (Height is Dim.DimAuto { _min: null })
+ {
+ ThrowInvalid (view, view.Height, nameof (view.Height));
+ ThrowInvalid (view, view.Y, nameof (view.Y));
+ }
+ }
+ }
+
private void LayoutSubview (View v, Rectangle contentArea)
{
//if (v.LayoutStyle == LayoutStyle.Computed) {
@@ -1424,36 +1521,64 @@ public partial class View
v.LayoutNeeded = false;
}
- #region Diagnostics
-
- // Diagnostics to highlight when Width or Height is read before the view has been initialized
- private Dim VerifyIsInitialized (Dim dim, string member)
+ /// Resizes the View to fit the specified size. Factors in the HotKey.
+ /// ResizeBoundsToFit can only be called when AutoSize is true (or being set to true).
+ ///
+ /// whether the Bounds was changed or not
+ private bool ResizeBoundsToFit (Size size)
{
-#if DEBUG
- if (LayoutStyle == LayoutStyle.Computed && !IsInitialized)
+ //if (AutoSize == false) {
+ // throw new InvalidOperationException ("ResizeBoundsToFit can only be called when AutoSize is true");
+ //}
+
+ var boundsChanged = false;
+ bool canSizeW = TrySetWidth (size.Width - GetHotKeySpecifierLength (), out int rW);
+ bool canSizeH = TrySetHeight (size.Height - GetHotKeySpecifierLength (false), out int rH);
+
+ if (canSizeW)
{
- Debug.WriteLine (
- $"WARNING: \"{this}\" has not been initialized; {member} is indeterminate: {dim}. This is potentially a bug."
- );
+ boundsChanged = true;
+ _width = rW;
}
-#endif // DEBUG
- return dim;
+
+ if (canSizeH)
+ {
+ boundsChanged = true;
+ _height = rH;
+ }
+
+ if (boundsChanged)
+ {
+ Bounds = new (Bounds.X, Bounds.Y, canSizeW ? rW : Bounds.Width, canSizeH ? rH : Bounds.Height);
+ }
+
+ return boundsChanged;
}
// Diagnostics to highlight when X or Y is read before the view has been initialized
private Pos VerifyIsInitialized (Pos pos, string member)
{
#if DEBUG
- if (LayoutStyle == LayoutStyle.Computed && !IsInitialized)
+ if (pos is not Pos.PosAbsolute && LayoutStyle == LayoutStyle.Computed && !IsInitialized)
{
- Debug.WriteLine (
- $"WARNING: \"{this}\" has not been initialized; {member} is indeterminate {pos}. This is potentially a bug."
- );
+ Debug.WriteLine ($"WARNING: \"{this}\" has not been initialized; {member} is indeterminate ({pos}). This is potentially a bug.");
}
#endif // DEBUG
return pos;
}
+ // Diagnostics to highlight when Width or Height is read before the view has been initialized
+ private Dim VerifyIsInitialized (Dim dim, string member)
+ {
+#if DEBUG
+ if (dim is not Dim.DimAbsolute && LayoutStyle == LayoutStyle.Computed && !IsInitialized)
+ {
+ Debug.WriteLine ($"WARNING: \"{this}\" has not been initialized; {member} is indeterminate: ({dim}). This is potentially a bug.");
+ }
+#endif // DEBUG
+ return dim;
+ }
+
/// Gets or sets whether validation of and occurs.
///
/// Setting this to will enable validation of , ,
@@ -1462,6 +1587,4 @@ public partial class View
/// thus should only be used for debugging.
///
public bool ValidatePosDim { get; set; }
-
- #endregion
}
diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs
index b9cacc711..11b3abd01 100644
--- a/Terminal.Gui/View/ViewSubViews.cs
+++ b/Terminal.Gui/View/ViewSubViews.cs
@@ -85,6 +85,7 @@ public partial class View
view.EndInit ();
}
+ CheckDimAuto ();
SetNeedsLayout ();
SetNeedsDisplay ();
}
diff --git a/Terminal.Gui/View/ViewText.cs b/Terminal.Gui/View/ViewText.cs
index 598e2c76b..2306d02d9 100644
--- a/Terminal.Gui/View/ViewText.cs
+++ b/Terminal.Gui/View/ViewText.cs
@@ -1,13 +1,14 @@
-namespace Terminal.Gui;
+namespace Terminal.Gui;
public partial class View
{
private string _text;
///
- /// Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved or not when
- /// is enabled. If trailing spaces at the end of wrapped
- /// lines will be removed when is formatted for display. The default is .
+ /// Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved
+ /// or not when is enabled.
+ /// If trailing spaces at the end of wrapped lines will be removed when
+ /// is formatted for display. The default is .
///
public virtual bool PreserveTrailingSpaces
{
@@ -22,19 +23,29 @@ public partial class View
}
}
- /// The text displayed by the .
+ ///
+ /// The text displayed by the .
+ ///
///
- /// The text will be drawn before any subviews are drawn.
///
- /// The text will be drawn starting at the view origin (0, 0) and will be formatted according to
- /// and .
+ /// The text will be drawn before any subviews are drawn.
+ ///
+ ///
+ /// The text will be drawn starting at the view origin (0, 0) and will be formatted according
+ /// to and .
///
///
/// The text will word-wrap to additional lines if it does not fit horizontally. If 's height
/// is 1, the text will be clipped.
///
- /// If is true, the will be adjusted to fit the text.
- /// When the text changes, the is fired.
+ ///
+ /// Set the to enable hotkey support. To disable hotkey support set
+ /// to
+ /// (Rune)0xffff.
+ ///
+ ///
+ /// If is true, the will be adjusted to fit the text.
+ ///
///
public virtual string Text
{
@@ -80,7 +91,9 @@ public partial class View
/// redisplay the .
///
///
- /// If is true, the will be adjusted to fit the text.
+ ///
+ /// If is true, the will be adjusted to fit the text.
+ ///
///
/// The text alignment.
public virtual TextAlignment TextAlignment
@@ -99,7 +112,9 @@ public partial class View
/// .
///
///
- /// If is true, the will be adjusted to fit the text.
+ ///
+ /// If is true, the will be adjusted to fit the text.
+ ///
///
/// The text alignment.
public virtual TextDirection TextDirection
@@ -112,15 +127,20 @@ public partial class View
}
}
- /// Gets the used to format .
+ ///
+ /// Gets or sets the used to format .
+ ///
public TextFormatter TextFormatter { get; init; } = new ();
///
/// Gets or sets how the View's is aligned vertically when drawn. Changing this property will
- /// redisplay the .
+ /// redisplay
+ /// the .
///
///
- /// If is true, the will be adjusted to fit the text.
+ ///
+ /// If is true, the will be adjusted to fit the text.
+ ///
///
/// The text alignment.
public virtual VerticalTextAlignment VerticalTextAlignment
@@ -134,12 +154,47 @@ public partial class View
}
///
- /// Gets the Frame dimensions required to fit within using the text
- /// specified by the property and accounting for any
- /// characters.
+ /// Gets the width or height of the characters
+ /// in the property.
///
- /// The the needs to be set to fit the text.
- public Size GetAutoSize ()
+ ///
+ /// Only the first HotKey specifier found in is supported.
+ ///
+ ///
+ /// 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)
+ {
+ return TextFormatter.IsHorizontalDirection (TextDirection) && TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
+ ? Math.Max (HotKeySpecifier.GetColumns (), 0)
+ : 0;
+ }
+
+ return TextFormatter.IsVerticalDirection (TextDirection) && TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
+ ? Math.Max (HotKeySpecifier.GetColumns (), 0)
+ : 0;
+ }
+
+ // TODO: Refactor this to return the Bounds size, not Frame. Move code that accounts for
+ // TODO: Thickness out to callers.
+ ///
+ /// Gets the size of the required to fit within using the
+ /// text
+ /// formatting settings of and accounting for .
+ ///
+ ///
+ ///
+ /// The of the required to fit the formatted text.
+ public Size GetTextAutoSize ()
{
var x = 0;
var y = 0;
@@ -154,58 +209,19 @@ public partial class View
int newWidth = rect.Size.Width
- GetHotKeySpecifierLength ()
- + (Margin == null
- ? 0
- : Margin.Thickness.Horizontal
- + Border.Thickness.Horizontal
- + Padding.Thickness.Horizontal);
+ + (Margin == null ? 0 : Margin.Thickness.Horizontal + Border.Thickness.Horizontal + Padding.Thickness.Horizontal);
int newHeight = rect.Size.Height
- GetHotKeySpecifierLength (false)
- + (Margin == null
- ? 0
- : Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical);
+ + (Margin == null ? 0 : Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical);
return new (newWidth, newHeight);
}
///
- /// Gets the width or height of the characters in the
- /// property.
+ /// Can be overridden if the has
+ /// different format than the default.
///
- ///
- ///
- /// This is for , not . For to show the hotkey,
- /// set View. to the desired character.
- ///
- ///
- /// Only the first HotKey specifier found in is supported.
- ///
- ///
- ///
- /// 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)
- {
- return TextFormatter.IsHorizontalDirection (TextDirection) && TextFormatter.Text?.Contains ((char)TextFormatter.HotKeySpecifier.Value) == true
- ? Math.Max (TextFormatter.HotKeySpecifier.GetColumns (), 0)
- : 0;
- }
-
- return TextFormatter.IsVerticalDirection (TextDirection) && TextFormatter.Text?.Contains ((char)TextFormatter.HotKeySpecifier.Value) == true
- ? Math.Max (TextFormatter.HotKeySpecifier.GetColumns (), 0)
- : 0;
- }
-
- /// Can be overridden if the has different format than the default.
protected virtual void UpdateTextFormatterText ()
{
if (TextFormatter is { })
@@ -214,23 +230,25 @@ public partial class View
}
}
- /// Gets the dimensions required for ignoring a .
+ ///
+ /// Gets the dimensions required for ignoring a .
+ ///
///
internal Size GetSizeNeededForTextWithoutHotKey ()
{
- return new (
- TextFormatter.Size.Width - GetHotKeySpecifierLength (),
- TextFormatter.Size.Height - GetHotKeySpecifierLength (false)
- );
+ return new Size (
+ TextFormatter.Size.Width - GetHotKeySpecifierLength (),
+ TextFormatter.Size.Height - GetHotKeySpecifierLength (false));
}
///
- /// Internal API. Sets .Size to the current size, adjusted for
+ /// Sets .Size to the current size, adjusted for
/// .
///
///
- /// Use this API to set when the view has changed such that the size required to
- /// fit the text has changed. changes.
+ /// Use this API to set when the view has changed such that the
+ /// size required to fit the text has changed.
+ /// changes.
///
///
internal void SetTextFormatterSize ()
@@ -249,20 +267,45 @@ public partial class View
return;
}
- TextFormatter.Size = new (
- Bounds.Size.Width + GetHotKeySpecifierLength (),
- Bounds.Size.Height + GetHotKeySpecifierLength (false)
- );
+ int w = Bounds.Size.Width + GetHotKeySpecifierLength ();
+
+ if (Width is Dim.DimAuto widthAuto && widthAuto._style != Dim.DimAutoStyle.Subviews)
+ {
+ if (Height is Dim.DimAuto)
+ {
+ // Both are auto.
+ TextFormatter.Size = new Size (SuperView?.Bounds.Width ?? 0, SuperView?.Bounds.Height ?? 0);
+ }
+ else
+ {
+ TextFormatter.Size = new Size (SuperView?.Bounds.Width ?? 0, Bounds.Size.Height + GetHotKeySpecifierLength ());
+ }
+
+ w = TextFormatter.FormatAndGetSize ().Width;
+ }
+ else
+ {
+ TextFormatter.Size = new Size (w, SuperView?.Bounds.Height ?? 0);
+ }
+
+ int h = Bounds.Size.Height + GetHotKeySpecifierLength ();
+
+ if (Height is Dim.DimAuto heightAuto && heightAuto._style != Dim.DimAutoStyle.Subviews)
+ {
+ TextFormatter.NeedsFormat = true;
+ h = TextFormatter.FormatAndGetSize ().Height;
+ }
+
+ TextFormatter.Size = new Size (w, h);
}
private bool IsValidAutoSize (out Size autoSize)
{
Rectangle rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
- autoSize = new (
- rect.Size.Width - GetHotKeySpecifierLength (),
- rect.Size.Height - GetHotKeySpecifierLength (false)
- );
+ autoSize = new Size (
+ rect.Size.Width - GetHotKeySpecifierLength (),
+ rect.Size.Height - GetHotKeySpecifierLength (false));
return !((ValidatePosDim && (!(Width is Dim.DimAbsolute) || !(Height is Dim.DimAbsolute)))
|| _frame.Size.Width != rect.Size.Width - GetHotKeySpecifierLength ()
@@ -274,8 +317,7 @@ public partial class View
Rectangle rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
int dimValue = height.Anchor (0);
- return !((ValidatePosDim && !(height is Dim.DimAbsolute))
- || dimValue != rect.Size.Height - GetHotKeySpecifierLength (false));
+ return !((ValidatePosDim && !(height is Dim.DimAbsolute)) || dimValue != rect.Size.Height - GetHotKeySpecifierLength (false));
}
private bool IsValidAutoSizeWidth (Dim width)
@@ -283,27 +325,24 @@ public partial class View
Rectangle rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
int dimValue = width.Anchor (0);
- return !((ValidatePosDim && !(width is Dim.DimAbsolute))
- || dimValue != rect.Size.Width - GetHotKeySpecifierLength ());
+ return !((ValidatePosDim && !(width is Dim.DimAbsolute)) || dimValue != rect.Size.Width - GetHotKeySpecifierLength ());
}
- /// Sets the size of the View to the minimum width or height required to fit .
+ ///
+ /// Sets the size of the View to the minimum width or height required to fit .
+ ///
///
/// if the size was changed; if ==
- /// or will not fit.
+ /// or
+ /// will not fit.
///
///
- /// Always returns if is or if
- /// (Horizontal) or (Vertical) are not not set or zero. Does not take into
- /// account word wrapping.
+ /// Always returns if is or
+ /// if (Horizontal) or (Vertical) are not not set or zero.
+ /// Does not take into account word wrapping.
///
private bool SetFrameToFitText ()
{
- if (AutoSize == false)
- {
- throw new InvalidOperationException ("SetFrameToFitText can only be called when AutoSize is true");
- }
-
// BUGBUG: This API is broken - should not assume Frame.Height == Bounds.Height
//
// Gets the minimum dimensions required to fit the View's , factoring in .
@@ -334,7 +373,7 @@ public partial class View
switch (TextFormatter.IsVerticalDirection (TextDirection))
{
case true:
- int colWidth = TextFormatter.GetWidestLineLength (new List { TextFormatter.Text }, 0, 1);
+ int colWidth = TextFormatter.GetSumMaxCharWidth (TextFormatter.Text, 0, 1);
// TODO: v2 - This uses frame.Width; it should only use Bounds
if (_frame.Width < colWidth
@@ -374,25 +413,29 @@ public partial class View
return false;
}
- // only called from EndInit
private void UpdateTextDirection (TextDirection newDirection)
{
- bool directionChanged = TextFormatter.IsHorizontalDirection (TextFormatter.Direction)
- != TextFormatter.IsHorizontalDirection (newDirection);
+ bool directionChanged = TextFormatter.IsHorizontalDirection (TextFormatter.Direction) != TextFormatter.IsHorizontalDirection (newDirection);
TextFormatter.Direction = newDirection;
bool isValidOldAutoSize = AutoSize && IsValidAutoSize (out Size _);
UpdateTextFormatterText ();
- if ((!ValidatePosDim && directionChanged && AutoSize)
- || (ValidatePosDim && directionChanged && AutoSize && isValidOldAutoSize))
+ if ((!ValidatePosDim && directionChanged && AutoSize) || (ValidatePosDim && directionChanged && AutoSize && isValidOldAutoSize))
{
OnResizeNeeded ();
}
- else if (AutoSize && directionChanged && IsAdded)
+ else if (directionChanged && IsAdded)
{
ResizeBoundsToFit (Bounds.Size);
+
+ // BUGBUG: I think this call is redundant.
+ SetFrameToFitText ();
+ }
+ else
+ {
+ SetFrameToFitText ();
}
SetTextFormatterSize ();
diff --git a/Terminal.Gui/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs
index b22480ccc..b333b981a 100644
--- a/Terminal.Gui/Views/Dialog.cs
+++ b/Terminal.Gui/Views/Dialog.cs
@@ -60,9 +60,13 @@ public class Dialog : Window
Y = Pos.Center ();
ValidatePosDim = true;
- Width = Dim.Percent (85); // Dim.Auto (min: Dim.Percent (10));
- Height = Dim.Percent (85); //Dim.Auto (min: Dim.Percent (50));
-
+#if DIMAUTO
+ Width = Dim.Auto (min: Dim.Percent (10));
+ Height = Dim.Auto (min: Dim.Percent (50));
+#else
+ Width = Dim.Percent (85);
+ Height = Dim.Percent (85);
+#endif
ColorScheme = Colors.ColorSchemes ["Dialog"];
Modal = true;
diff --git a/UICatalog/Scenarios/DimAutoDemo.cs b/UICatalog/Scenarios/DimAutoDemo.cs
new file mode 100644
index 000000000..41a19a720
--- /dev/null
+++ b/UICatalog/Scenarios/DimAutoDemo.cs
@@ -0,0 +1,197 @@
+using System;
+using Terminal.Gui;
+using static Terminal.Gui.Dim;
+
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("DimAuto", "Demonstrates Dim.Auto")]
+[ScenarioCategory ("Layout")]
+public class DimAutoDemo : Scenario
+{
+ public override void Main ()
+ {
+ Application.Init ();
+ // Setup - Create a top-level application window and configure it.
+ Window appWindow = new ()
+ {
+ Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
+ };
+
+ var view = new FrameView
+ {
+ Title = "Type to make View grow",
+ X = 1,
+ Y = 1,
+ Width = Auto (DimAutoStyle.Subviews, 40),
+ Height = Auto (DimAutoStyle.Subviews, 10)
+ };
+ view.ValidatePosDim = true;
+
+ var textEdit = new TextView { Text = "", X = 1, Y = 0, Width = 20, Height = 4 };
+ view.Add (textEdit);
+
+ var hlabel = new Label
+ {
+ Text = textEdit.Text,
+ X = Pos.Left (textEdit) + 1,
+ Y = Pos.Bottom (textEdit),
+ AutoSize = false,
+ Width = Auto (DimAutoStyle.Text, 20),
+ Height = 1,
+ ColorScheme = Colors.ColorSchemes ["Error"]
+ };
+ view.Add (hlabel);
+
+ var vlabel = new Label
+ {
+ Text = textEdit.Text,
+ X = Pos.Left (textEdit),
+ Y = Pos.Bottom (textEdit) + 1,
+ AutoSize = false,
+ Width = 1,
+ Height = Auto (DimAutoStyle.Text, 8),
+ ColorScheme = Colors.ColorSchemes ["Error"]
+
+ //TextDirection = TextDirection.TopBottom_LeftRight
+ };
+ vlabel.Id = "vlabel";
+ view.Add (vlabel);
+
+ var heightAuto = new View
+ {
+ X = Pos.Right (vlabel) + 1,
+ Y = Pos.Bottom (hlabel) + 1,
+ Width = 20,
+ Height = Auto (),
+ ColorScheme = Colors.ColorSchemes ["Error"],
+ Title = "W: 20, H: Auto",
+ BorderStyle = LineStyle.Rounded
+ };
+ heightAuto.Id = "heightAuto";
+ view.Add (heightAuto);
+
+ var widthAuto = new View
+ {
+ X = Pos.Right (heightAuto) + 1,
+ Y = Pos.Bottom (hlabel) + 1,
+ Width = Auto (),
+ Height = 5,
+ ColorScheme = Colors.ColorSchemes ["Error"],
+ Title = "W: Auto, H: 5",
+ BorderStyle = LineStyle.Rounded
+ };
+ widthAuto.Id = "widthAuto";
+ view.Add (widthAuto);
+
+ var bothAuto = new View
+ {
+ X = Pos.Right (widthAuto) + 1,
+ Y = Pos.Bottom (hlabel) + 1,
+ Width = Auto (),
+ Height = Auto (),
+ ColorScheme = Colors.ColorSchemes ["Error"],
+ Title = "W: Auto, H: Auto",
+ BorderStyle = LineStyle.Rounded
+ };
+ bothAuto.Id = "bothAuto";
+ view.Add (bothAuto);
+
+ textEdit.ContentsChanged += (s, e) =>
+ {
+ hlabel.Text = textEdit.Text;
+ vlabel.Text = textEdit.Text;
+ heightAuto.Text = textEdit.Text;
+ widthAuto.Text = textEdit.Text;
+ bothAuto.Text = textEdit.Text;
+ };
+
+ var movingButton = new Button
+ {
+ Text = "_Move down",
+ X = Pos.Right (vlabel),
+ Y = Pos.Bottom (vlabel),
+ };
+ movingButton.Accept += (s, e) => { movingButton.Y = movingButton.Frame.Y + 1; };
+ view.Add (movingButton);
+
+ var resetButton = new Button
+ {
+ Text = "_Reset Button",
+ X = Pos.Right (movingButton),
+ Y = Pos.Top (movingButton)
+ };
+
+ resetButton.Accept += (s, e) => { movingButton.Y = Pos.Bottom (hlabel); };
+ view.Add (resetButton);
+
+ var dlgButton = new Button
+ {
+ Text = "Open Test _Dialog",
+ X = Pos.Right (view),
+ Y = Pos.Top (view)
+ };
+ dlgButton.Accept += DlgButton_Clicked;
+
+ appWindow.Add (view, dlgButton);
+
+ // Run - Start the application.
+ Application.Run (appWindow);
+ appWindow.Dispose ();
+
+ // Shutdown - Calling Application.Shutdown is required.
+ Application.Shutdown ();
+
+ }
+
+ private void DlgButton_Clicked (object sender, EventArgs e)
+ {
+ var dlg = new Dialog
+ {
+ Title = "Test Dialog"
+ };
+
+ //var ok = new Button ("Bye") { IsDefault = true };
+ //ok.Clicked += (s, _) => Application.RequestStop (dlg);
+ //dlg.AddButton (ok);
+
+ //var cancel = new Button ("Abort") { };
+ //cancel.Clicked += (s, _) => Application.RequestStop (dlg);
+ //dlg.AddButton (cancel);
+
+ var label = new Label
+ {
+ ValidatePosDim = true,
+ Text = "This is a label (AutoSize = false; Dim.Auto(3/20). Press Esc to close. Even more text.",
+ AutoSize = false,
+ X = Pos.Center (),
+ Y = 0,
+ Height = Auto (min: 3),
+ Width = Auto (min: 20),
+ ColorScheme = Colors.ColorSchemes ["Menu"]
+ };
+
+ var text = new TextField
+ {
+ ValidatePosDim = true,
+ Text = "TextField: X=1; Y=Pos.Bottom (label)+1, Width=Dim.Fill (0); Height=1",
+ TextFormatter = new TextFormatter { WordWrap = true },
+ X = 0,
+ Y = Pos.Bottom (label) + 1,
+ Width = Fill (10),
+ Height = 1
+ };
+
+ //var btn = new Button
+ //{
+ // Text = "AnchorEnd", Y = Pos.AnchorEnd (1)
+ //};
+
+ //// TODO: We should really fix AnchorEnd to do this automatically.
+ //btn.X = Pos.AnchorEnd () - (Pos.Right (btn) - Pos.Left (btn));
+ dlg.Add (label);
+ dlg.Add (text);
+ //dlg.Add (btn);
+ Application.Run (dlg);
+ dlg.Dispose ();
+ }
+}
diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj
index 662411daf..5ce7889ea 100644
--- a/UICatalog/UICatalog.csproj
+++ b/UICatalog/UICatalog.csproj
@@ -1,4 +1,4 @@
-
+Exenet8.0
diff --git a/UnitTests/Dialogs/DialogTests.cs b/UnitTests/Dialogs/DialogTests.cs
index 6c7eb754a..dc4793c86 100644
--- a/UnitTests/Dialogs/DialogTests.cs
+++ b/UnitTests/Dialogs/DialogTests.cs
@@ -33,18 +33,18 @@ public class DialogTests
Width = width,
Height = 1,
ButtonAlignment = Dialog.ButtonAlignments.Center,
- Buttons = [new Button { Text = btn1Text }]
+ Buttons = [new () { Text = btn1Text }]
};
// Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
- dlg.Border.Thickness = new Thickness (1, 0, 1, 0);
+ dlg.Border.Thickness = new (1, 0, 1, 0);
runstate = Begin (dlg);
var buttonRow = $"{CM.Glyphs.VLine} {btn1} {CM.Glyphs.VLine}";
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
// Now add a second button
buttonRow = $"{CM.Glyphs.VLine} {btn1} {btn2} {CM.Glyphs.VLine}";
- dlg.AddButton (new Button { Text = btn2Text });
+ dlg.AddButton (new () { Text = btn2Text });
var first = false;
RunIteration (ref runstate, ref first);
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -52,24 +52,24 @@ public class DialogTests
dlg.Dispose ();
// Justify
- dlg = new Dialog
+ dlg = new ()
{
Title = title,
Width = width,
Height = 1,
ButtonAlignment = Dialog.ButtonAlignments.Justify,
- Buttons = [new Button { Text = btn1Text }]
+ Buttons = [new () { Text = btn1Text }]
};
// Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
- dlg.Border.Thickness = new Thickness (1, 0, 1, 0);
+ dlg.Border.Thickness = new (1, 0, 1, 0);
runstate = Begin (dlg);
buttonRow = $"{CM.Glyphs.VLine} {btn1}{CM.Glyphs.VLine}";
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
// Now add a second button
buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2}{CM.Glyphs.VLine}";
- dlg.AddButton (new Button { Text = btn2Text });
+ dlg.AddButton (new () { Text = btn2Text });
first = false;
RunIteration (ref runstate, ref first);
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -77,24 +77,24 @@ public class DialogTests
dlg.Dispose ();
// Right
- dlg = new Dialog
+ dlg = new ()
{
Title = title,
Width = width,
Height = 1,
ButtonAlignment = Dialog.ButtonAlignments.Right,
- Buttons = [new Button { Text = btn1Text }]
+ Buttons = [new () { Text = btn1Text }]
};
// Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
- dlg.Border.Thickness = new Thickness (1, 0, 1, 0);
+ dlg.Border.Thickness = new (1, 0, 1, 0);
runstate = Begin (dlg);
- buttonRow = $"{CM.Glyphs.VLine}{new string (' ', width - btn1.Length - 2)}{btn1}{CM.Glyphs.VLine}";
+ buttonRow = $"{CM.Glyphs.VLine}{new (' ', width - btn1.Length - 2)}{btn1}{CM.Glyphs.VLine}";
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
// Now add a second button
buttonRow = $"{CM.Glyphs.VLine} {btn1} {btn2}{CM.Glyphs.VLine}";
- dlg.AddButton (new Button { Text = btn2Text });
+ dlg.AddButton (new () { Text = btn2Text });
first = false;
RunIteration (ref runstate, ref first);
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -102,24 +102,24 @@ public class DialogTests
dlg.Dispose ();
// Left
- dlg = new Dialog
+ dlg = new ()
{
Title = title,
Width = width,
Height = 1,
ButtonAlignment = Dialog.ButtonAlignments.Left,
- Buttons = [new Button { Text = btn1Text }]
+ Buttons = [new () { Text = btn1Text }]
};
// Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
- dlg.Border.Thickness = new Thickness (1, 0, 1, 0);
+ dlg.Border.Thickness = new (1, 0, 1, 0);
runstate = Begin (dlg);
- buttonRow = $"{CM.Glyphs.VLine}{btn1}{new string (' ', width - btn1.Length - 2)}{CM.Glyphs.VLine}";
+ buttonRow = $"{CM.Glyphs.VLine}{btn1}{new (' ', width - btn1.Length - 2)}{CM.Glyphs.VLine}";
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
// Now add a second button
buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {CM.Glyphs.VLine}";
- dlg.AddButton (new Button { Text = btn2Text });
+ dlg.AddButton (new () { Text = btn2Text });
first = false;
RunIteration (ref runstate, ref first);
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -153,14 +153,14 @@ public class DialogTests
// Default - Center
(runstate, Dialog dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Center,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Center,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -170,14 +170,14 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Justify,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Justify,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -187,14 +187,14 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Right,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Right,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -204,14 +204,14 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Left,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Left,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -261,31 +261,17 @@ public class DialogTests
// Justify
buttonRow =
- $"{
- CM.Glyphs.VLine
- }{
- CM.Glyphs.LeftBracket
- } yes {
- CM.Glyphs.LeftBracket
- } no {
- CM.Glyphs.LeftBracket
- } maybe {
- CM.Glyphs.LeftBracket
- } never {
- CM.Glyphs.RightBracket
- }{
- CM.Glyphs.VLine
- }";
+ $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} yes {CM.Glyphs.LeftBracket} no {CM.Glyphs.LeftBracket} maybe {CM.Glyphs.LeftBracket} never {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Justify,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Justify,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -294,14 +280,14 @@ public class DialogTests
buttonRow = $"{CM.Glyphs.VLine}{CM.Glyphs.RightBracket} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Right,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Right,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -310,14 +296,14 @@ public class DialogTests
buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {CM.Glyphs.LeftBracket} n{CM.Glyphs.VLine}";
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Left,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Left,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -351,14 +337,14 @@ public class DialogTests
// Default - Center
(runstate, Dialog dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Center,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Center,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -368,14 +354,14 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Justify,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Justify,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -385,14 +371,14 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Right,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Right,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -402,14 +388,14 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Left,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Left,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -445,14 +431,14 @@ public class DialogTests
// Default - Center
(runstate, Dialog dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Center,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Center,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -462,14 +448,14 @@ public class DialogTests
Assert.Equal (width, buttonRow.GetColumns ());
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Justify,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Justify,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -479,14 +465,14 @@ public class DialogTests
Assert.Equal (width, buttonRow.GetColumns ());
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Right,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Right,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -496,14 +482,14 @@ public class DialogTests
Assert.Equal (width, buttonRow.GetColumns ());
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Left,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text },
- new Button { Text = btn4Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Left,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text },
+ new Button { Text = btn4Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -528,11 +514,11 @@ public class DialogTests
d.SetBufferSize (width, 1);
(runstate, Dialog dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Center,
- new Button { Text = btnText }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Center,
+ new Button { Text = btnText }
+ );
// Center
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -545,11 +531,11 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Justify,
- new Button { Text = btnText }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Justify,
+ new Button { Text = btnText }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -560,11 +546,11 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Right,
- new Button { Text = btnText }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Right,
+ new Button { Text = btnText }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -575,11 +561,11 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Left,
- new Button { Text = btnText }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Left,
+ new Button { Text = btnText }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -592,11 +578,11 @@ public class DialogTests
d.SetBufferSize (width, 1);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Center,
- new Button { Text = btnText }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Center,
+ new Button { Text = btnText }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -607,11 +593,11 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Justify,
- new Button { Text = btnText }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Justify,
+ new Button { Text = btnText }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -622,11 +608,11 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Right,
- new Button { Text = btnText }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Right,
+ new Button { Text = btnText }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -637,11 +623,11 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Left,
- new Button { Text = btnText }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Left,
+ new Button { Text = btnText }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -671,13 +657,13 @@ public class DialogTests
d.SetBufferSize (buttonRow.Length, 3);
(runstate, Dialog dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Center,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Center,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -687,13 +673,13 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Justify,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Justify,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -703,13 +689,13 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Right,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Right,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -719,13 +705,13 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Left,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text },
- new Button { Text = btn3Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Left,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text },
+ new Button { Text = btn3Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -753,12 +739,12 @@ public class DialogTests
d.SetBufferSize (buttonRow.Length, 3);
(runstate, Dialog dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Center,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Center,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -768,12 +754,12 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Justify,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Justify,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -783,12 +769,12 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Right,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Right,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -798,12 +784,12 @@ public class DialogTests
Assert.Equal (width, buttonRow.Length);
(runstate, dlg) = RunButtonTestDialog (
- title,
- width,
- Dialog.ButtonAlignments.Left,
- new Button { Text = btn1Text },
- new Button { Text = btn2Text }
- );
+ title,
+ width,
+ Dialog.ButtonAlignments.Left,
+ new Button { Text = btn1Text },
+ new Button { Text = btn2Text }
+ );
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
dlg.Dispose ();
@@ -835,8 +821,8 @@ public class DialogTests
Button button1, button2;
// Default (Center)
- button1 = new Button { Text = btn1Text };
- button2 = new Button { Text = btn2Text };
+ button1 = new () { Text = btn1Text };
+ button2 = new () { Text = btn2Text };
(runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Center, button1, button2);
button1.Visible = false;
RunIteration (ref runstate, ref firstIteration);
@@ -847,8 +833,8 @@ public class DialogTests
// Justify
Assert.Equal (width, buttonRow.Length);
- button1 = new Button { Text = btn1Text };
- button2 = new Button { Text = btn2Text };
+ button1 = new () { Text = btn1Text };
+ button2 = new () { Text = btn2Text };
(runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Justify, button1, button2);
button1.Visible = false;
RunIteration (ref runstate, ref firstIteration);
@@ -859,8 +845,8 @@ public class DialogTests
// Right
Assert.Equal (width, buttonRow.Length);
- button1 = new Button { Text = btn1Text };
- button2 = new Button { Text = btn2Text };
+ button1 = new () { Text = btn1Text };
+ button2 = new () { Text = btn2Text };
(runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Right, button1, button2);
button1.Visible = false;
RunIteration (ref runstate, ref firstIteration);
@@ -870,8 +856,8 @@ public class DialogTests
// Left
Assert.Equal (width, buttonRow.Length);
- button1 = new Button { Text = btn1Text };
- button2 = new Button { Text = btn2Text };
+ button1 = new () { Text = btn1Text };
+ button2 = new () { Text = btn2Text };
(runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Left, button1, button2);
button1.Visible = false;
RunIteration (ref runstate, ref firstIteration);
@@ -902,7 +888,7 @@ public class DialogTests
win.Loaded += (s, a) =>
{
- var dlg = new Dialog { Width = 18, Height = 3, Buttons = [new Button { Text = "Ok" }] };
+ var dlg = new Dialog { Width = 18, Height = 3, Buttons = [new () { Text = "Ok" }] };
dlg.Loaded += (s, a) =>
{
@@ -911,9 +897,7 @@ public class DialogTests
var expected = @$"
┌──────────────────┐
│┌────────────────┐│
-││ {
- btn
-} ││
+││ {btn} ││
│└────────────────┘│
└──────────────────┘";
_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
@@ -994,7 +978,12 @@ public class DialogTests
if (iterations == 0)
{
- var dlg = new Dialog { Buttons = [new Button { Text = "Ok" }] };
+ var dlg = new Dialog
+ {
+ Buttons = [new () { Text = "Ok" }],
+ Width = Dim.Percent (85),
+ Height = Dim.Percent (85)
+ };
Run (dlg);
}
else if (iterations == 1)
@@ -1023,34 +1012,33 @@ public class DialogTests
string expected = null;
btn1.Accept += (s, e) =>
- {
- btn2 = new Button { Text = "Show Sub" };
- btn3 = new Button { Text = "Close" };
- btn3.Accept += (s, e) => RequestStop ();
+ {
+ btn2 = new () { Text = "Show Sub" };
+ btn3 = new () { Text = "Close" };
+ btn3.Accept += (s, e) => RequestStop ();
- btn2.Accept += (s, e) =>
- {
- // Don't test MessageBox in Dialog unit tests!
- var subBtn = new Button { Text = "Ok", IsDefault = true };
- var subDlg = new Dialog { Text = "ya", Width = 20, Height = 5, Buttons = [subBtn] };
- subBtn.Accept += (s, e) => RequestStop (subDlg);
- Run (subDlg);
- };
- var dlg = new Dialog { Buttons = [btn2, btn3] };
+ btn2.Accept += (s, e) =>
+ {
+ // Don't test MessageBox in Dialog unit tests!
+ var subBtn = new Button { Text = "Ok", IsDefault = true };
+ var subDlg = new Dialog { Text = "ya", Width = 20, Height = 5, Buttons = [subBtn] };
+ subBtn.Accept += (s, e) => RequestStop (subDlg);
+ Run (subDlg);
+ };
- Run (dlg);
- };
+ var dlg = new Dialog
+ {
+ Buttons = [btn2, btn3],
+ Width = Dim.Percent (85),
+ Height = Dim.Percent (85)
+ };
+
+ Run (dlg);
+ dlg.Dispose ();
+ };
var btn =
- $"{
- CM.Glyphs.LeftBracket
- }{
- CM.Glyphs.LeftDefaultIndicator
- } Ok {
- CM.Glyphs.RightDefaultIndicator
- }{
- CM.Glyphs.RightBracket
- }";
+ $"{CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} Ok {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket}";
int iterations = -1;
@@ -1071,15 +1059,7 @@ public class DialogTests
│ │
│ │
│ │
- │{
- CM.Glyphs.LeftBracket
- } Show Sub {
- CM.Glyphs.RightBracket
- } {
- CM.Glyphs.LeftBracket
- } Close {
- CM.Glyphs.RightBracket
- } │
+ │{CM.Glyphs.LeftBracket} Show Sub {CM.Glyphs.RightBracket} {CM.Glyphs.LeftBracket} Close {CM.Glyphs.RightBracket} │
└───────────────────────┘";
TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
@@ -1093,19 +1073,9 @@ public class DialogTests
│ ┌──────────────────┐ │
│ │ya │ │
│ │ │ │
- │ │ {
- btn
- } │ │
+ │ │ {btn} │ │
│ └──────────────────┘ │
- │{
- CM.Glyphs.LeftBracket
- } Show Sub {
- CM.Glyphs.RightBracket
- } {
- CM.Glyphs.LeftBracket
- } Close {
- CM.Glyphs.RightBracket
- } │
+ │{CM.Glyphs.LeftBracket} Show Sub {CM.Glyphs.RightBracket} {CM.Glyphs.LeftBracket} Close {CM.Glyphs.RightBracket} │
└───────────────────────┘",
_output
);
@@ -1149,13 +1119,17 @@ public class DialogTests
[AutoInitShutdown]
public void Location_Default ()
{
- var d = new Dialog ();
+ var d = new Dialog ()
+ {
+ Width = Dim.Percent (85),
+ Height = Dim.Percent (85)
+ };
Begin (d);
((FakeDriver)Driver).SetBufferSize (100, 100);
// Default location is centered, so 100 / 2 - 85 / 2 = 7
var expected = 7;
- Assert.Equal (new Point (expected, expected), (Point)d.Frame.Location);
+ Assert.Equal (new (expected, expected), d.Frame.Location);
}
[Fact]
@@ -1168,7 +1142,7 @@ public class DialogTests
// Default location is centered, so 100 / 2 - 85 / 2 = 7
var expected = 1;
- Assert.Equal (new Point (expected, expected), (Point)d.Frame.Location);
+ Assert.Equal (new (expected, expected), d.Frame.Location);
}
[Fact]
@@ -1181,7 +1155,7 @@ public class DialogTests
((FakeDriver)Driver).SetBufferSize (20, 10);
// Default location is centered, so 100 / 2 - 85 / 2 = 7
- Assert.Equal (new Point (expected, expected), (Point)d.Frame.Location);
+ Assert.Equal (new (expected, expected), d.Frame.Location);
TestHelpers.AssertDriverContentsWithFrameAre (
@"
@@ -1210,9 +1184,9 @@ public class DialogTests
if (iterations == 0)
{
var d = new Dialog { X = 5, Y = 5, Height = 3, Width = 5 };
- var rs = Begin (d);
+ RunState rs = Begin (d);
- Assert.Equal (new Point (5, 5), (Point)d.Frame.Location);
+ Assert.Equal (new (5, 5), d.Frame.Location);
TestHelpers.AssertDriverContentsWithFrameAre (
@"
@@ -1231,7 +1205,13 @@ public class DialogTests
End (rs);
d.Dispose ();
- d = new Dialog { X = 5, Y = 5 };
+ d = new ()
+ {
+ X = 5, Y = 5,
+ Width = Dim.Percent (85),
+ Height = Dim.Percent (85)
+
+ };
rs = Begin (d);
// This is because of PostionTopLevels and EnsureVisibleBounds
@@ -1312,12 +1292,18 @@ public class DialogTests
[AutoInitShutdown]
public void Size_Default ()
{
- var d = new Dialog ();
+ var d = new Dialog ()
+ {
+ Width = Dim.Percent (85),
+ Height = Dim.Percent (85)
+ };
+
Begin (d);
((FakeDriver)Driver).SetBufferSize (100, 100);
// Default size is Percent(85)
Assert.Equal (new ((int)(100 * .85), (int)(100 * .85)), d.Frame.Size);
+ d.Dispose ();
}
[Fact]
@@ -1331,10 +1317,11 @@ public class DialogTests
// Default size is Percent(85)
Assert.Equal (new (50, 50), d.Frame.Size);
+ d.Dispose ();
}
[Fact]
- [AutoInitShutdown]
+ [SetupFakeDriver]
public void Zero_Buttons_Works ()
{
RunState runstate = null;
@@ -1347,10 +1334,11 @@ public class DialogTests
int width = buttonRow.Length;
d.SetBufferSize (buttonRow.Length, 3);
- (runstate, Dialog _) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Center, null);
+ (runstate, Dialog dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Center, null);
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
End (runstate);
+ dlg.Dispose ();
}
private (RunState, Dialog) RunButtonTestDialog (
@@ -1372,7 +1360,7 @@ public class DialogTests
};
// Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
- dlg.Border.Thickness = new Thickness (1, 0, 1, 0);
+ dlg.Border.Thickness = new (1, 0, 1, 0);
return (Begin (dlg), dlg);
}
@@ -1410,15 +1398,12 @@ public class DialogTests
Assert.True (Top.WasDisposed);
Assert.NotNull (Top);
#endif
- Shutdown();
+ Shutdown ();
Assert.Null (Top);
return;
- void Dlg_Ready (object sender, EventArgs e)
- {
- RequestStop ();
- }
+ void Dlg_Ready (object sender, EventArgs e) { RequestStop (); }
}
[Fact]
@@ -1446,10 +1431,11 @@ public class DialogTests
#if DEBUG_IDISPOSABLE
Dialog dlg2 = new ();
dlg2.Ready += Dlg_Ready;
- var exception = Record.Exception (() => Run (dlg2));
+ Exception exception = Record.Exception (() => Run (dlg2));
Assert.NotNull (exception);
- dlg.Dispose();
+ dlg.Dispose ();
+
// Now it's possible to tun dlg2 without throw
Run (dlg2);
@@ -1459,6 +1445,7 @@ public class DialogTests
Assert.False (dlg2.WasDisposed);
dlg2.Dispose ();
+
// Now an assertion will throw accessing the Canceled property
exception = Record.Exception (() => Assert.True (dlg.Canceled));
Assert.NotNull (exception);
diff --git a/UnitTests/View/Layout/DimAutoTests.cs b/UnitTests/View/Layout/DimAutoTests.cs
new file mode 100644
index 000000000..bbb3d6c56
--- /dev/null
+++ b/UnitTests/View/Layout/DimAutoTests.cs
@@ -0,0 +1,545 @@
+using System.Globalization;
+using System.Text;
+using Xunit.Abstractions;
+
+// Alias Console to MockConsole so we don't accidentally use Console
+using Console = Terminal.Gui.FakeConsole;
+
+namespace Terminal.Gui.ViewTests;
+
+public class DimAutoTests
+{
+ private readonly ITestOutputHelper _output;
+
+ public DimAutoTests (ITestOutputHelper output)
+ {
+ _output = output;
+ Console.OutputEncoding = Encoding.Default;
+
+ // Change current culture
+ var culture = CultureInfo.CreateSpecificCulture ("en-US");
+ Thread.CurrentThread.CurrentCulture = culture;
+ Thread.CurrentThread.CurrentUICulture = culture;
+ }
+
+ // Test min - ensure that if min is specified in the DimAuto constructor it is honored
+ [Fact]
+ public void DimAuto_Min ()
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (min: 10),
+ Height = Dim.Auto (min: 10),
+ ValidatePosDim = true
+ };
+
+ var subView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = 5,
+ Height = 5
+ };
+
+ superView.Add (subView);
+ superView.BeginInit ();
+ superView.EndInit ();
+
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ superView.LayoutSubviews (); // no throw
+
+ Assert.Equal (10, superView.Frame.Width);
+ Assert.Equal (10, superView.Frame.Height);
+ }
+
+ // what happens if DimAuto (min: 10) and the subview moves to a negative coord?
+ [Fact]
+ public void DimAuto_Min_Resets_If_Subview_Moves_Negative ()
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (min: 10),
+ Height = Dim.Auto (min: 10),
+ ValidatePosDim = true
+ };
+
+ var subView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = 5,
+ Height = 5
+ };
+
+ superView.Add (subView);
+ superView.BeginInit ();
+ superView.EndInit ();
+
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ superView.LayoutSubviews (); // no throw
+
+ Assert.Equal (10, superView.Frame.Width);
+ Assert.Equal (10, superView.Frame.Height);
+
+ subView.X = -1;
+ subView.Y = -1;
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ superView.LayoutSubviews (); // no throw
+
+ Assert.Equal (5, subView.Frame.Width);
+ Assert.Equal (5, subView.Frame.Height);
+
+ Assert.Equal (10, superView.Frame.Width);
+ Assert.Equal (10, superView.Frame.Height);
+ }
+
+ [Fact]
+ public void DimAuto_Min_Resets_If_Subview_Shrinks ()
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (min: 10),
+ Height = Dim.Auto (min: 10),
+ ValidatePosDim = true
+ };
+
+ var subView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = 5,
+ Height = 5
+ };
+
+ superView.Add (subView);
+ superView.BeginInit ();
+ superView.EndInit ();
+
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ superView.LayoutSubviews (); // no throw
+
+ Assert.Equal (10, superView.Frame.Width);
+ Assert.Equal (10, superView.Frame.Height);
+
+ subView.Width = 3;
+ subView.Height = 3;
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ superView.LayoutSubviews (); // no throw
+
+ Assert.Equal (3, subView.Frame.Width);
+ Assert.Equal (3, subView.Frame.Height);
+
+ Assert.Equal (10, superView.Frame.Width);
+ Assert.Equal (10, superView.Frame.Height);
+ }
+
+ [Theory]
+ [InlineData (0, 0, 0, 0, 0)]
+ [InlineData (0, 0, 5, 0, 0)]
+ [InlineData (0, 0, 0, 5, 5)]
+ [InlineData (0, 0, 5, 5, 5)]
+ [InlineData (1, 0, 5, 0, 0)]
+ [InlineData (1, 0, 0, 5, 5)]
+ [InlineData (1, 0, 5, 5, 5)]
+ [InlineData (1, 1, 5, 5, 6)]
+ [InlineData (-1, 0, 5, 0, 0)]
+ [InlineData (-1, 0, 0, 5, 5)]
+ [InlineData (-1, 0, 5, 5, 5)]
+ [InlineData (-1, -1, 5, 5, 4)]
+ public void Height_Auto_Width_NotChanged (int subX, int subY, int subWidth, int subHeight, int expectedHeight)
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = 10,
+ Height = Dim.Auto (),
+ ValidatePosDim = true
+ };
+
+ var subView = new View
+ {
+ X = subX,
+ Y = subY,
+ Width = subWidth,
+ Height = subHeight,
+ ValidatePosDim = true
+ };
+
+ superView.Add (subView);
+
+ superView.BeginInit ();
+ superView.EndInit ();
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ Assert.Equal (new Rectangle (0, 0, 10, expectedHeight), superView.Frame);
+ }
+
+ [Fact]
+ public void NoSubViews_Does_Nothing ()
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (),
+ Height = Dim.Auto (),
+ ValidatePosDim = true
+ };
+
+ superView.BeginInit ();
+ superView.EndInit ();
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ Assert.Equal (new Rectangle (0, 0, 0, 0), superView.Frame);
+
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ Assert.Equal (new Rectangle (0, 0, 0, 0), superView.Frame);
+
+ superView.SetRelativeLayout (new Rectangle (10, 10, 10, 10));
+ Assert.Equal (new Rectangle (0, 0, 0, 0), superView.Frame);
+ }
+
+ [Theory]
+ [InlineData (0, 0, 0, 0, 0, 0)]
+ [InlineData (0, 0, 5, 0, 5, 0)]
+ [InlineData (0, 0, 0, 5, 0, 5)]
+ [InlineData (0, 0, 5, 5, 5, 5)]
+ [InlineData (1, 0, 5, 0, 6, 0)]
+ [InlineData (1, 0, 0, 5, 1, 5)]
+ [InlineData (1, 0, 5, 5, 6, 5)]
+ [InlineData (1, 1, 5, 5, 6, 6)]
+ [InlineData (-1, 0, 5, 0, 4, 0)]
+ [InlineData (-1, 0, 0, 5, 0, 5)]
+ [InlineData (-1, 0, 5, 5, 4, 5)]
+ [InlineData (-1, -1, 5, 5, 4, 4)]
+ public void SubView_ChangesSuperViewSize (int subX, int subY, int subWidth, int subHeight, int expectedWidth, int expectedHeight)
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (),
+ Height = Dim.Auto (),
+ ValidatePosDim = true
+ };
+
+ var subView = new View
+ {
+ X = subX,
+ Y = subY,
+ Width = subWidth,
+ Height = subHeight,
+ ValidatePosDim = true
+ };
+
+ superView.Add (subView);
+
+ superView.BeginInit ();
+ superView.EndInit ();
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ Assert.Equal (new Rectangle (0, 0, expectedWidth, expectedHeight), superView.Frame);
+ }
+
+ // Test validation
+ [Fact]
+ public void ValidatePosDim_True_Throws_When_SubView_Uses_SuperView_Dims ()
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (),
+ Height = Dim.Auto (),
+ ValidatePosDim = true
+ };
+
+ var subView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (),
+ Height = 10,
+ ValidatePosDim = true
+ };
+
+ superView.BeginInit ();
+ superView.EndInit ();
+ Assert.Throws (() => superView.Add (subView));
+
+ subView.Width = 10;
+ superView.Add (subView);
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ superView.LayoutSubviews (); // no throw
+
+ subView.Width = Dim.Fill ();
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.Width = 10;
+
+ subView.Height = Dim.Fill ();
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.Height = 10;
+
+ subView.Height = Dim.Percent (50);
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.Height = 10;
+
+ subView.X = Pos.Center ();
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.X = 0;
+
+ subView.Y = Pos.Center ();
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.Y = 0;
+
+ subView.Width = 10;
+ subView.Height = 10;
+ subView.X = 0;
+ subView.Y = 0;
+ superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0));
+ superView.LayoutSubviews ();
+ }
+
+ // Test validation
+ [Fact]
+ public void ValidatePosDim_True_Throws_When_SubView_Uses_SuperView_Dims_Combine ()
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (),
+ Height = Dim.Auto (),
+ ValidatePosDim = true
+ };
+
+ var subView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = 10,
+ Height = 10
+ };
+
+ var subView2 = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = 10,
+ Height = 10
+ };
+
+ superView.Add (subView, subView2);
+ superView.BeginInit ();
+ superView.EndInit ();
+ superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0));
+ superView.LayoutSubviews (); // no throw
+
+ subView.Height = Dim.Fill () + 3;
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.Height = 0;
+
+ subView.Height = 3 + Dim.Fill ();
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.Height = 0;
+
+ subView.Height = 3 + 5 + Dim.Fill ();
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.Height = 0;
+
+ subView.Height = 3 + 5 + Dim.Percent (10);
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.Height = 0;
+
+ // Tests nested Combine
+ subView.Height = 5 + new Dim.DimCombine (true, 3, new Dim.DimCombine (true, Dim.Percent (10), 9));
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ }
+
+ [Fact]
+ public void ValidatePosDim_True_Throws_When_SubView_Uses_SuperView_Pos_Combine ()
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (),
+ Height = Dim.Auto (),
+ ValidatePosDim = true
+ };
+
+ var subView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = 10,
+ Height = 10
+ };
+
+ var subView2 = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = 10,
+ Height = 10
+ };
+
+ superView.Add (subView, subView2);
+ superView.BeginInit ();
+ superView.EndInit ();
+ superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0));
+ superView.LayoutSubviews (); // no throw
+
+ subView.X = Pos.Right (subView2);
+ superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0));
+ superView.LayoutSubviews (); // no throw
+
+ subView.X = Pos.Right (subView2) + 3;
+ superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)); // no throw
+ superView.LayoutSubviews (); // no throw
+
+ subView.X = new Pos.PosCombine (true, Pos.Right (subView2), new Pos.PosCombine (true, 7, 9));
+ superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)); // no throw
+
+ subView.X = Pos.Center () + 3;
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.X = 0;
+
+ subView.X = 3 + Pos.Center ();
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.X = 0;
+
+ subView.X = 3 + 5 + Pos.Center ();
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.X = 0;
+
+ subView.X = 3 + 5 + Pos.Percent (10);
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.X = 0;
+
+ subView.X = Pos.Percent (10) + Pos.Center ();
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.X = 0;
+
+ // Tests nested Combine
+ subView.X = 5 + new Pos.PosCombine (true, Pos.Right (subView2), new Pos.PosCombine (true, Pos.Center (), 9));
+ Assert.Throws (() => superView.SetRelativeLayout (new Rectangle (0, 0, 0, 0)));
+ subView.X = 0;
+ }
+
+ [Theory]
+ [InlineData (0, 0, 0, 0, 0)]
+ [InlineData (0, 0, 5, 0, 5)]
+ [InlineData (0, 0, 0, 5, 0)]
+ [InlineData (0, 0, 5, 5, 5)]
+ [InlineData (1, 0, 5, 0, 6)]
+ [InlineData (1, 0, 0, 5, 1)]
+ [InlineData (1, 0, 5, 5, 6)]
+ [InlineData (1, 1, 5, 5, 6)]
+ [InlineData (-1, 0, 5, 0, 4)]
+ [InlineData (-1, 0, 0, 5, 0)]
+ [InlineData (-1, 0, 5, 5, 4)]
+ [InlineData (-1, -1, 5, 5, 4)]
+ public void Width_Auto_Height_NotChanged (int subX, int subY, int subWidth, int subHeight, int expectedWidth)
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (),
+ Height = 10,
+ ValidatePosDim = true
+ };
+
+ var subView = new View
+ {
+ X = subX,
+ Y = subY,
+ Width = subWidth,
+ Height = subHeight,
+ ValidatePosDim = true
+ };
+
+ superView.Add (subView);
+
+ superView.BeginInit ();
+ superView.EndInit ();
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 10));
+ Assert.Equal (new Rectangle (0, 0, expectedWidth, 10), superView.Frame);
+ }
+
+ // Test that when a view has Width set to DimAuto (min: x) the width is never < x even if SetRelativeLayout is called with smaller bounds
+ [Theory]
+ [InlineData (0, 0)]
+ [InlineData (1, 1)]
+ [InlineData (3, 3)]
+ [InlineData (4, 4)]
+ [InlineData (5, 4)] // This is clearly invalid, but we choose to not throw but log a debug message
+ public void Width_Auto_Min (int min, int expectedWidth)
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (min: min),
+ Height = 1,
+ ValidatePosDim = true
+ };
+
+ superView.BeginInit ();
+ superView.EndInit ();
+ superView.SetRelativeLayout (new Rectangle (0, 0, 4, 1));
+ Assert.Equal (expectedWidth, superView.Frame.Width);
+ }
+
+ // Test Dim.Fill - Fill should not impact width of the DimAuto superview
+ [Theory]
+ [InlineData (0, 0, 0, 10, 10)]
+ [InlineData (0, 1, 0, 10, 10)]
+ [InlineData (0, 11, 0, 10, 10)]
+ [InlineData (0, 10, 0, 10, 10)]
+ [InlineData (0, 5, 0, 10, 10)]
+ [InlineData (1, 5, 0, 10, 9)]
+ [InlineData (1, 10, 0, 10, 9)]
+ [InlineData (0, 0, 1, 10, 9)]
+ [InlineData (0, 10, 1, 10, 9)]
+ [InlineData (0, 5, 1, 10, 9)]
+ [InlineData (1, 5, 1, 10, 8)]
+ [InlineData (1, 10, 1, 10, 8)]
+ public void Width_Fill_Fills (int subX, int superMinWidth, int fill, int expectedSuperWidth, int expectedSubWidth)
+ {
+ var superView = new View
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Auto (min:superMinWidth),
+ Height = 1,
+ ValidatePosDim = true
+ };
+
+ var subView = new View
+ {
+ X = subX,
+ Y = 0,
+ Width = Dim.Fill (fill),
+ Height = 1,
+ ValidatePosDim = true
+ };
+
+ superView.Add (subView);
+
+ superView.BeginInit ();
+ superView.EndInit ();
+ superView.SetRelativeLayout (new Rectangle (0, 0, 10, 1));
+ Assert.Equal (expectedSuperWidth, superView.Frame.Width);
+ superView.LayoutSubviews ();
+ Assert.Equal (expectedSubWidth, subView.Frame.Width);
+ Assert.Equal (expectedSuperWidth, superView.Frame.Width);
+ }
+
+ // Test variations of Frame
+}
diff --git a/UnitTests/View/Layout/DimTests.cs b/UnitTests/View/Layout/DimTests.cs
index 40bcb2ecf..8e66bf17a 100644
--- a/UnitTests/View/Layout/DimTests.cs
+++ b/UnitTests/View/Layout/DimTests.cs
@@ -1,9 +1,7 @@
using System.Globalization;
using System.Text;
using Xunit.Abstractions;
-
-// Alias Console to MockConsole so we don't accidentally use Console
-using Console = Terminal.Gui.FakeConsole;
+using static Terminal.Gui.Dim;
namespace Terminal.Gui.ViewTests;
@@ -26,7 +24,7 @@ public class DimTests
// A new test that does not depend on Application is needed.
[Fact]
[AutoInitShutdown]
- public void Dim_Add_Operator ()
+ public void Add_Operator ()
{
Toplevel top = new ();
@@ -76,176 +74,7 @@ public class DimTests
// TODO: A new test that calls SetRelativeLayout directly is needed.
[Fact]
[TestRespondersDisposed]
- public void Dim_Referencing_SuperView_Does_Not_Throw ()
- {
- var super = new View { Width = 10, Height = 10, Text = "super" };
-
- var view = new View
- {
- Width = Dim.Width (super), // this is allowed
- Height = Dim.Height (super), // this is allowed
- Text = "view"
- };
-
- super.Add (view);
- super.BeginInit ();
- super.EndInit ();
-
- Exception exception = Record.Exception (super.LayoutSubviews);
- Assert.Null (exception);
- super.Dispose ();
- }
-
- // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
- // TODO: A new test that calls SetRelativeLayout directly is needed.
- [Fact]
- [AutoInitShutdown]
- public void Dim_Subtract_Operator ()
- {
- Toplevel top = new ();
-
- var view = new View { X = 0, Y = 0, Width = 20, Height = 0 };
- var field = new TextField { X = 0, Y = Pos.Bottom (view), Width = 20 };
- var count = 20;
- List