diff --git a/.editorconfig b/.editorconfig
index ed21eef37..d30be450e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -96,15 +96,7 @@ csharp_style_var_for_built_in_types = true:none
resharper_wrap_before_linq_expression = true
resharper_wrap_chained_binary_expressions = chop_if_long
resharper_wrap_chained_binary_patterns = chop_if_long
-resharper_xmldoc_indent_size = 2
-resharper_xmldoc_indent_style = space
-resharper_xmldoc_indent_text = DoNotTouch
-resharper_xmldoc_linebreaks_inside_tags_for_elements_longer_than = 120
-resharper_xmldoc_max_blank_lines_between_tags = 1
-resharper_xmldoc_max_line_length = 100
-resharper_xmldoc_space_before_self_closing = false
-resharper_xmldoc_tab_width = 2
-resharper_xmldoc_use_indent_from_vs = true
+
[*.{cs,vb}]
dotnet_style_operator_placement_when_wrapping = beginning_of_line
diff --git a/Terminal.Gui/View/Layout/PosDim.cs b/Terminal.Gui/View/Layout/PosDim.cs
index fa60c6683..98ec74c1c 100644
--- a/Terminal.Gui/View/Layout/PosDim.cs
+++ b/Terminal.Gui/View/Layout/PosDim.cs
@@ -3,113 +3,327 @@
namespace Terminal.Gui;
///
-/// Describes the position of a which can be an absolute value, a percentage, centered, or
-/// relative to the ending dimension. Integer values are implicitly convertible to
-/// an absolute . These objects are created using the static methods Percent,
-/// AnchorEnd, and Center. The objects can be combined with the addition and
-/// subtraction operators.
+/// Describes the position of a which can be an absolute value, a percentage, centered, or
+/// relative to the ending dimension. Integer values are implicitly convertible to
+/// an absolute . These objects are created using the static methods Percent,
+/// AnchorEnd, and Center. The objects can be combined with the addition and
+/// subtraction operators.
///
///
-///
-/// Use the objects on the X or Y properties of a view to control the position.
-///
-///
-/// These can be used to set the absolute position, when merely assigning an
-/// integer value (via the implicit integer to conversion), and they can be combined
-/// to produce more useful layouts, like: Pos.Center - 3, which would shift the position
-/// of the 3 characters to the left after centering for example.
-///
-///
-/// Reference coordinates of another view by using the methods Left(View), Right(View), Bottom(View), Top(View). The X(View) and Y(View) are
-/// aliases to Left(View) and Top(View) respectively.
-///
-///
-///
-///
-/// Pos Object
-/// Description
-///
-///
-///
-///
-/// Creates a object that computes the position by executing the provided function. The function will be called every time the position is needed.
-///
-///
-///
-///
-///
-/// Creates a object that is a percentage of the width or height of the SuperView.
-///
-///
-///
-///
-///
-/// Creates a object that is anchored to the end (right side or bottom) of the dimension,
-/// useful to flush the layout from the right or bottom.
-///
-///
-///
-///
-///
-/// Creates a object that can be used to center the .
-///
-///
-///
-///
-///
-/// Creates a object that is an absolute position based on the specified integer value.
-///
-///
-///
-///
-///
-/// Creates a object that tracks the Left (X) position of the specified .
-///
-///
-///
-///
-///
-/// Creates a object that tracks the Left (X) position of the specified .
-///
-///
-///
-///
-///
-/// Creates a object that tracks the Top (Y) position of the specified .
-///
-///
-///
-///
-///
-/// Creates a object that tracks the Top (Y) position of the specified .
-///
-///
-///
-///
-///
-/// Creates a object that tracks the Right (X+Width) coordinate of the specified .
-///
-///
-///
-///
-///
-/// Creates a object that tracks the Bottom (Y+Height) coordinate of the specified
-///
-///
-///
-///
-///
+///
+/// Use the objects on the X or Y properties of a view to control the position.
+///
+///
+/// These can be used to set the absolute position, when merely assigning an
+/// integer value (via the implicit integer to conversion), and they can be combined
+/// to produce more useful layouts, like: Pos.Center - 3, which would shift the position
+/// of the 3 characters to the left after centering for example.
+///
+///
+/// Reference coordinates of another view by using the methods Left(View), Right(View), Bottom(View),
+/// Top(View). The X(View) and Y(View) are
+/// aliases to Left(View) and Top(View) respectively.
+///
+///
+///
+///
+/// Pos Object
+/// Description
+///
+///
+///
+///
+///
+///
+/// Creates a object that computes the position by executing the
+/// provided function. The function will be called every time the position is
+/// needed.
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that is a percentage of the width or height
+/// of the SuperView.
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that is anchored to the end (right side or
+/// bottom) of the dimension,
+/// useful to flush the layout from the right or bottom.
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that can be used to center the
+/// .
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that is an absolute position based on the
+/// specified integer value.
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that tracks the Left (X) position of the
+/// specified .
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that tracks the Left (X) position of the
+/// specified .
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that tracks the Top (Y) position of the
+/// specified .
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that tracks the Top (Y) position of the
+/// specified .
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that tracks the Right (X+Width) coordinate
+/// of the specified .
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that tracks the Bottom (Y+Height)
+/// coordinate of the specified
+///
+///
+///
+///
+///
///
public class Pos {
internal virtual int Anchor (int width) => 0;
///
- /// Creates a object that computes the position by executing the provided function. The function will be called every time the position is needed.
+ /// Creates a object that computes the position by executing the provided function. The function
+ /// will be called every time the position is needed.
///
/// The function to be executed.
- /// The returned from the function.
+ /// The returned from the function.
public static Pos Function (Func function) => new PosFunc (function);
+ ///
+ /// Creates a percentage object
+ ///
+ /// The percent object.
+ /// 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 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),
+ /// };
+ ///
+ ///
+ public static Pos Percent (float n)
+ {
+ if (n is < 0 or > 100) {
+ throw new ArgumentException ("Percent value must be between 0 and 100");
+ }
+
+ return new PosFactor (n / 100);
+ }
+
+ ///
+ /// Creates a object that is anchored to the end (right side or bottom) of the dimension,
+ /// useful to flush the layout from the right or bottom.
+ ///
+ /// The object anchored to the end (the bottom or the right side).
+ /// 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);
+ ///
+ ///
+ public static Pos AnchorEnd (int offset = 0)
+ {
+ if (offset < 0) {
+ throw new ArgumentException (@"Must be positive", nameof (offset));
+ }
+
+ return new PosAnchorEnd (offset);
+ }
+
+ ///
+ /// 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 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),
+ /// };
+ ///
+ ///
+ public static Pos Center () => new PosCenter ();
+
+ ///
+ /// Creates a object that is an absolute position based on the specified integer value.
+ ///
+ /// The Absolute .
+ /// The value to convert to the .
+ public static Pos At (int n) => new PosAbsolute (n);
+
+ ///
+ /// Creates an Absolute from the specified integer value.
+ ///
+ /// The Absolute .
+ /// The value to convert to the .
+ public static implicit operator Pos (int n) => new PosAbsolute (n);
+
+ ///
+ /// 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 Pos operator + (Pos left, Pos right)
+ {
+ if (left is PosAbsolute && right is PosAbsolute) {
+ return new PosAbsolute (left.Anchor (0) + right.Anchor (0));
+ }
+ var newPos = new PosCombine (true, left, right);
+ SetPosCombine (left, newPos);
+ return newPos;
+ }
+
+ ///
+ /// Subtracts a from a , yielding a new
+ /// .
+ ///
+ /// The to subtract from (the minuend).
+ /// The to subtract (the subtrahend).
+ /// The that is the left minus right.
+ public static Pos operator - (Pos left, Pos right)
+ {
+ if (left is PosAbsolute && right is PosAbsolute) {
+ return new PosAbsolute (left.Anchor (0) - right.Anchor (0));
+ }
+ var newPos = new PosCombine (false, left, right);
+ SetPosCombine (left, newPos);
+ return newPos;
+ }
+
+ static void SetPosCombine (Pos left, PosCombine newPos)
+ {
+ var view = left as PosView;
+ if (view != null) {
+ view.Target.SetNeedsLayout ();
+ }
+ }
+
+ ///
+ /// 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) => new PosView (view, 0);
+
+ ///
+ /// 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) => new PosView (view, 0);
+
+ ///
+ /// 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) => new PosView (view, 1);
+
+ ///
+ /// 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) => new PosView (view, 1);
+
+ ///
+ /// Creates a object that tracks the Right (X+Width) coordinate of the specified
+ /// .
+ ///
+ /// The that depends on the other view.
+ /// The that will be tracked.
+ public static Pos Right (View view) => new PosView (view, 2);
+
+ ///
+ /// Creates a object that tracks the Bottom (Y+Height) coordinate of the specified
+ ///
+ ///
+ /// The that depends on the other view.
+ /// The that will be tracked.
+ public static Pos Bottom (View view) => new PosView (view, 3);
+
+ /// Serves as the default hash function.
+ /// A hash code for the current object.
+ public override int GetHashCode () => Anchor (0).GetHashCode ();
+
+ /// Determines whether the specified object is equal to the current object.
+ /// The object to compare with the current object.
+ ///
+ /// if the specified object is equal to the current object; otherwise,
+ /// .
+ ///
+ public override bool Equals (object other) => other is Pos abs && abs == this;
+
internal class PosFactor : Pos {
readonly float _factor;
@@ -139,55 +353,6 @@ public class Pos {
public override bool Equals (object other) => other is PosFunc f && f._function () == _function ();
}
- ///
- /// Creates a percentage object
- ///
- /// The percent object.
- /// 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 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),
- /// };
- ///
- ///
- public static Pos Percent (float n)
- {
- if (n is < 0 or > 100) {
- throw new ArgumentException ("Percent value must be between 0 and 100");
- }
-
- return new PosFactor (n / 100);
- }
-
- ///
- /// Creates a object that is anchored to the end (right side or bottom) of the dimension,
- /// useful to flush the layout from the right or bottom.
- ///
- /// The object anchored to the end (the bottom or the right side).
- /// 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);
- ///
- ///
- public static Pos AnchorEnd (int offset = 0)
- {
- if (offset < 0) {
- throw new ArgumentException (@"Must be positive", nameof(offset));
- }
-
- return new PosAnchorEnd (offset);
- }
-
internal class PosAnchorEnd : Pos {
readonly int _offset;
@@ -202,24 +367,6 @@ public class Pos {
public override bool Equals (object other) => other is PosAnchorEnd anchorEnd && anchorEnd._offset == _offset;
}
- ///
- /// 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 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),
- /// };
- ///
- ///
- public static Pos Center () => new PosCenter ();
-
internal class PosAbsolute : Pos {
readonly int _n;
public PosAbsolute (int n) => _n = n;
@@ -239,16 +386,9 @@ public class Pos {
public override string ToString () => "Center";
}
- ///
- /// Creates a object that is an absolute position based on the specified integer value.
- ///
- /// The Absolute .
- /// The value to convert to the .
- public static Pos At (int n) => new PosAbsolute (n);
-
internal class PosCombine : Pos {
- internal Pos _left, _right;
internal bool _add;
+ internal Pos _left, _right;
public PosCombine (bool add, Pos left, Pos right)
{
@@ -259,68 +399,20 @@ public class Pos {
internal override int Anchor (int width)
{
- int la = _left.Anchor (width);
- int ra = _right.Anchor (width);
+ var la = _left.Anchor (width);
+ var ra = _right.Anchor (width);
if (_add) {
return la + ra;
- } else {
- return la - ra;
}
+ return la - ra;
}
public override string ToString () => $"Combine({_left}{(_add ? '+' : '-')}{_right})";
}
- ///
- /// Creates an Absolute from the specified integer value.
- ///
- /// The Absolute .
- /// The value to convert to the .
- public static implicit operator Pos (int n) => new PosAbsolute (n);
-
- ///
- /// 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 Pos operator + (Pos left, Pos right)
- {
- if (left is PosAbsolute && right is PosAbsolute) {
- return new PosAbsolute (left.Anchor (0) + right.Anchor (0));
- }
- var newPos = new PosCombine (true, left, right);
- SetPosCombine (left, newPos);
- return newPos;
- }
-
- ///
- /// Subtracts a from a , yielding a new .
- ///
- /// The to subtract from (the minuend).
- /// The to subtract (the subtrahend).
- /// The that is the left minus right.
- public static Pos operator - (Pos left, Pos right)
- {
- if (left is PosAbsolute && right is PosAbsolute) {
- return new PosAbsolute (left.Anchor (0) - right.Anchor (0));
- }
- var newPos = new PosCombine (false, left, right);
- SetPosCombine (left, newPos);
- return newPos;
- }
-
- static void SetPosCombine (Pos left, PosCombine newPos)
- {
- var view = left as PosView;
- if (view != null) {
- view.Target.SetNeedsLayout ();
- }
- }
-
internal class PosView : Pos {
public readonly View Target;
- int side;
+ readonly int side;
public PosView (View view, int side)
{
@@ -330,76 +422,276 @@ public class Pos {
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;
- default:
- return 0;
- }
+ return side switch {
+ 0 => Target.Frame.X,
+ 1 => Target.Frame.Y,
+ 2 => Target.Frame.Right,
+ 3 => Target.Frame.Bottom,
+ _ => 0
+ };
}
- 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;
- }
- // Note: We do not checkt `Target` for null here to intentionally throw if so
- return $"View(side={tside},target={Target.ToString ()})";
- }
+ public override string ToString ()
+ {
+ string tside = side switch {
+ 0 => "x",
+ 1 => "y",
+ 2 => "right",
+ 3 => "bottom",
+ _ => "unknown"
+ };
+ // Note: We do not checkt `Target` for null here to intentionally throw if so
+ return $"View(side={tside},target={Target})";
+ }
public override int GetHashCode () => Target.GetHashCode ();
public override bool Equals (object other) => other is PosView abs && abs.Target == Target;
}
+}
+///
+///
+/// A Dim object describes the dimensions of a . Dim is the type of the
+/// and
+/// properties of . Dim objects enable Computed Layout (see
+/// )
+/// to automatically manage the dimensions of a view.
+///
+///
+/// Integer values are implicitly convertible to an absolute . These objects are created
+/// using the static methods described below.
+/// The objects can be combined with the addition and subtraction operators.
+///
+///
+///
+///
+///
+///
+/// Dim Object
+/// Description
+///
+///
+///
+///
+///
+///
+/// Creates a object that computes the dimension by executing
+/// the provided function. The function will be called every time the dimension is
+/// needed.
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that is a percentage of the width or height
+/// of the SuperView.
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that fills the dimension, leaving the
+/// specified number of columns for a margin.
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that automatically sizes the view to fit
+/// all of the view's SubViews.
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that tracks the Width of the specified
+/// .
+///
+///
+///
+///
+///
+///
+///
+/// Creates a object that tracks the Height of the specified
+/// .
+///
+///
+///
+///
+///
+///
+///
+public class Dim {
+ ///
+ /// Specifies how will compute the dimension.
+ ///
+ public enum DimAutoStyle {
///
- /// 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) => new PosView (view, 0);
-
- ///
- /// Creates a object that tracks the Left (X) position of the specified .
+ /// The dimension will be computed from the view's . NOT CURRENTLY SUPPORTED.
///
- /// The that depends on the other view.
- /// The that will be tracked.
- public static Pos X (View view) => new PosView (view, 0);
+ Text,
///
- /// Creates a object that tracks the Top (Y) position of the specified .
+ /// The dimension will be computed from the view's .
///
- /// The that depends on the other view.
- /// The that will be tracked.
- public static Pos Top (View view) => new PosView (view, 1);
+ Subviews
+ }
- ///
- /// 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) => new PosView(view, 1);
+ internal virtual int Anchor (int width) => 0;
- ///
- /// Creates a object that tracks the Right (X+Width) coordinate of the specified .
- ///
- /// The that depends on the other view.
- /// The that will be tracked.
- public static Pos Right (View view) => new PosView (view, 2);
+ ///
+ /// Creates a function object that computes the dimension by executing the provided function.
+ /// The function will be called every time the dimension is needed.
+ ///
+ /// The function to be executed.
+ /// The returned from the function.
+ public static Dim Function (Func function) => new DimFunc (function);
- ///
- /// Creates a object that tracks the Bottom (Y+Height) coordinate of the specified
- ///
- /// The that depends on the other view.
- /// The that will be tracked.
- public static Pos Bottom (View view) => new PosView (view, 3);
+ ///
+ /// 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.
+ ///
+ ///
+ /// 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),
+ /// };
+ ///
+ ///
+ public static Dim Percent (float n, bool r = false)
+ {
+ if (n is < 0 or > 100) {
+ throw new ArgumentException ("Percent value must be between 0 and 100");
+ }
+
+ return new DimFactor (n / 100, r);
+ }
+
+ ///
+ /// Creates a object that fills the dimension, leaving the specified number of columns for a
+ /// margin.
+ ///
+ /// The Fill dimension.
+ /// Margin to use.
+ public static Dim Fill (int margin = 0) => new DimFill (margin);
+
+ ///
+ /// Creates a object that automatically sizes the view to fit all of the view's SubViews.
+ ///
+ ///
+ /// 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
+ /// . NOT CURRENTLY SUPPORTED.
+ ///
+ /// Specifies the minimum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED.
+ /// Specifies the maximum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED.
+ public static Dim Auto (DimAutoStyle style = DimAutoStyle.Subviews, Dim min = null, Dim max = null)
+ {
+ if (style == DimAutoStyle.Text) {
+ throw new NotImplementedException (@"DimAutoStyle.Text is not implemented.");
+ }
+ //if (min != null) {
+ // throw new NotImplementedException (@"min is not implemented");
+ //}
+ if (max != null) {
+ throw new NotImplementedException (@"max is not implemented");
+ }
+ return new DimAuto (style, min, max);
+ }
+
+ ///
+ /// Creates an Absolute from the specified integer value.
+ ///
+ /// The Absolute .
+ /// The value to convert to the pos.
+ public static implicit operator Dim (int n) => new DimAbsolute (n);
+
+ ///
+ /// Creates an Absolute from the specified integer value.
+ ///
+ /// The Absolute .
+ /// The value to convert to the .
+ public static Dim Sized (int n) => new DimAbsolute (n);
+
+ ///
+ /// 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)
+ {
+ if (left is DimAbsolute && right is DimAbsolute) {
+ return new DimAbsolute (left.Anchor (0) + right.Anchor (0));
+ }
+ var newDim = new DimCombine (true, left, right);
+ SetDimCombine (left, newDim);
+ return newDim;
+ }
+
+ ///
+ /// Subtracts a from a , yielding a new
+ /// .
+ ///
+ /// 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)
+ {
+ if (left is DimAbsolute && right is DimAbsolute) {
+ return new DimAbsolute (left.Anchor (0) - right.Anchor (0));
+ }
+ var newDim = new DimCombine (false, left, right);
+ SetDimCombine (left, newDim);
+ return newDim;
+ }
+
+ // BUGBUG: newPos is never used.
+ static void SetDimCombine (Dim left, DimCombine newPos) => (left as DimView)?.Target.SetNeedsLayout ();
+
+ ///
+ /// 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) => new DimView (view, 1);
+
+ ///
+ /// 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) => new DimView (view, 0);
/// Serves as the default hash function.
/// A hash code for the current object.
@@ -408,79 +700,10 @@ public class Pos {
/// Determines whether the specified object is equal to the current object.
/// The object to compare with the current object.
///
- /// if the specified object is equal to the current object; otherwise, .
- public override bool Equals (object other) => other is Pos abs && abs == this;
-}
-
-///
-///
-/// A Dim object describes the dimensions of a . Dim is the type of the and
-/// properties of . Dim objects enable Computed Layout (see )
-/// to automatically manage the dimensions of a view.
-///
-///
-/// Integer values are implicitly convertible to an absolute . These objects are created using the static methods described below.
-/// The objects can be combined with the addition and subtraction operators.
-///
-///
-///
-///
-///
-///
-/// Dim Object
-/// Description
-///
-///
-///
-///
-/// Creates a object that computes the dimension by executing the provided function. The function will be called every time the dimension is needed.
-///
-///
-///
-///
-///
-/// Creates a object that is a percentage of the width or height of the SuperView.
-///
-///
-///
-///
-///
-/// Creates a object that fills the dimension, leaving the specified number of columns for a margin.
-///
-///
-///
-///
-///
-/// Creates a object that automatically sizes the view to fit all of the view's SubViews.
-///
-///
-///
-///
-///
-/// Creates a object that tracks the Width of the specified .
-///
-///
-///
-///
-///
-/// Creates a object that tracks the Height of the specified .
-///
-///
-///
-///
-///
-///
-///
-public class Dim {
- internal virtual int Anchor (int width) => 0;
-
- ///
- /// Creates a function object that computes the dimension by executing the provided function.
- /// The function will be called every time the dimension is needed.
- ///
- /// The function to be executed.
- /// The returned from the function.
- public static Dim Function (Func function) => new DimFunc (function);
+ /// if the specified object is equal to the current object; otherwise,
+ /// .
+ ///
+ public override bool Equals (object other) => other is Dim abs && abs == this;
// Helper class to provide dynamic value by the execution of a function that returns an integer.
internal class DimFunc : Dim {
@@ -497,34 +720,6 @@ public class Dim {
public override bool Equals (object other) => other is DimFunc f && f._function () == _function ();
}
- ///
- /// 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.
- ///
- /// 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),
- /// };
- ///
- ///
- public static Dim Percent (float n, bool r = false)
- {
- if (n is < 0 or > 100) {
- throw new ArgumentException ("Percent value must be between 0 and 100");
- }
-
- return new DimFactor (n / 100, r);
- }
-
internal class DimFactor : Dim {
readonly float _factor;
readonly bool _remaining;
@@ -573,61 +768,9 @@ public class Dim {
public override bool Equals (object other) => other is DimFill fill && fill._margin == _margin;
}
- ///
- /// Creates a object that fills the dimension, leaving the specified number of columns for a margin.
- ///
- /// The Fill dimension.
- /// Margin to use.
- public static Dim Fill (int margin = 0) => new DimFill (margin);
-
- ///
- /// Creates a object that automatically sizes the view to fit all of the view's SubViews.
- ///
- ///
- /// 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 . NOT CURRENTLY SUPPORTED.
- /// Specifies the minimum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED.
- /// Specifies the maximum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED.
- public static Dim Auto (DimAutoStyle style = DimAutoStyle.Subviews, Dim min = null, Dim max = null)
- {
- if (style == DimAutoStyle.Text) {
- throw new NotImplementedException (@"DimAutoStyle.Text is not implemented.");
- }
- //if (min != null) {
- // throw new NotImplementedException (@"min is not implemented");
- //}
- if (max != null) {
- throw new NotImplementedException (@"max is not implemented");
- }
- return new DimAuto (style, min, max);
- }
-
- ///
- /// Specifies how will compute the dimension.
- ///
- public enum DimAutoStyle {
- ///
- /// The dimension will be computed from the view's . NOT CURRENTLY SUPPORTED.
- ///
- Text,
-
- ///
- /// The dimension will be computed from the view's .
- ///
- Subviews,
- }
-
internal class DimAuto : Dim {
- internal readonly Dim _min;
internal readonly Dim _max;
+ internal readonly Dim _min;
internal readonly DimAutoStyle _style;
public DimAuto (DimAutoStyle style, Dim min, Dim max)
@@ -641,26 +784,12 @@ public class Dim {
public override int GetHashCode () => HashCode.Combine (base.GetHashCode (), _min, _max, _style);
- public override bool Equals (object other) => other is DimAuto auto && (auto._min == _min && auto._max == _max && auto._style == _style);
+ public override bool Equals (object other) => other is DimAuto auto && auto._min == _min && auto._max == _max && auto._style == _style;
}
- ///
- /// Creates an Absolute from the specified integer value.
- ///
- /// The Absolute .
- /// The value to convert to the pos.
- public static implicit operator Dim (int n) => new DimAbsolute (n);
-
- ///
- /// Creates an Absolute from the specified integer value.
- ///
- /// The Absolute .
- /// The value to convert to the .
- public static Dim Sized (int n) => new DimAbsolute (n);
-
internal class DimCombine : Dim {
- internal Dim _left, _right;
internal bool _add;
+ internal Dim _left, _right;
public DimCombine (bool add, Dim left, Dim right)
{
@@ -671,55 +800,18 @@ public class Dim {
internal override int Anchor (int width)
{
- int la = _left.Anchor (width);
- int ra = _right.Anchor (width);
+ var la = _left.Anchor (width);
+ var ra = _right.Anchor (width);
if (_add) {
return la + ra;
- } else {
- return la - ra;
}
+ return la - ra;
}
public override string ToString () => $"Combine({_left}{(_add ? '+' : '-')}{_right})";
}
- ///
- /// 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)
- {
- if (left is DimAbsolute && right is DimAbsolute) {
- return new DimAbsolute (left.Anchor (0) + right.Anchor (0));
- }
- var newDim = new DimCombine (true, left, right);
- SetDimCombine (left, newDim);
- return newDim;
- }
-
- ///
- /// Subtracts a from a , yielding a new .
- ///
- /// 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)
- {
- if (left is DimAbsolute && right is DimAbsolute) {
- return new DimAbsolute (left.Anchor (0) - right.Anchor (0));
- }
- var newDim = new DimCombine (false, left, right);
- SetDimCombine (left, newDim);
- return newDim;
- }
-
- // BUGBUG: newPos is never used.
- static void SetDimCombine (Dim left, DimCombine newPos) => (left as DimView)?.Target.SetNeedsLayout ();
-
internal class DimView : Dim {
- public View Target { get; init; }
readonly int _side;
public DimView (View view, int side)
@@ -728,6 +820,8 @@ public class Dim {
_side = side;
}
+ public View Target { get; init; }
+
internal override int Anchor (int width) => _side switch {
0 => Target.Frame.Height,
1 => Target.Frame.Width,
@@ -739,7 +833,7 @@ public class Dim {
if (Target == null) {
throw new NullReferenceException ();
}
- string tside = _side switch {
+ var tside = _side switch {
0 => "Height",
1 => "Width",
_ => "unknown"
@@ -751,28 +845,4 @@ public class Dim {
public override bool Equals (object other) => other is DimView abs && abs.Target == Target;
}
-
- ///
- /// 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) => new DimView (view, 1);
-
- ///
- /// 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) => new DimView (view, 0);
-
- /// Serves as the default hash function.
- /// A hash code for the current object.
- public override int GetHashCode () => Anchor (0).GetHashCode ();
-
- /// Determines whether the specified object is equal to the current object.
- /// The object to compare with the current object.
- ///
- /// if the specified object is equal to the current object; otherwise, .
- public override bool Equals (object other) => other is Dim abs && abs == this;
}
\ No newline at end of file
diff --git a/UnitTests/View/Layout/DimTests.cs b/UnitTests/View/Layout/DimTests.cs
index f12452e11..8c0df1bcc 100644
--- a/UnitTests/View/Layout/DimTests.cs
+++ b/UnitTests/View/Layout/DimTests.cs
@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.Text;
using System.Threading;
using Xunit;
using Xunit.Abstractions;
-
// Alias Console to MockConsole so we don't accidentally use Console
using Console = Terminal.Gui.FakeConsole;
@@ -13,718 +13,7 @@ namespace Terminal.Gui.ViewTests;
public class DimTests {
readonly ITestOutputHelper _output;
- public DimTests (ITestOutputHelper output)
- {
- _output = output;
- Console.OutputEncoding = System.Text.Encoding.Default;
- // Change current culture
- var culture = CultureInfo.CreateSpecificCulture ("en-US");
- Thread.CurrentThread.CurrentCulture = culture;
- Thread.CurrentThread.CurrentUICulture = culture;
- }
-
- [Fact]
- public void New_Works ()
- {
- var dim = new Dim ();
- Assert.Equal ("Terminal.Gui.Dim", dim.ToString ());
- }
-
- [Fact]
- public void Sized_SetsValue ()
- {
- var dim = Dim.Sized (0);
- Assert.Equal ("Absolute(0)", dim.ToString ());
-
- int testVal = 5;
- dim = Dim.Sized (testVal);
- Assert.Equal ($"Absolute({testVal})", dim.ToString ());
-
- testVal = -1;
- dim = Dim.Sized (testVal);
- Assert.Equal ($"Absolute({testVal})", dim.ToString ());
- }
-
- [Fact]
- public void Sized_Equals ()
- {
- int n1 = 0;
- int n2 = 0;
- var dim1 = Dim.Sized (n1);
- var dim2 = Dim.Sized (n2);
- Assert.Equal (dim1, dim2);
-
- n1 = n2 = 1;
- dim1 = Dim.Sized (n1);
- dim2 = Dim.Sized (n2);
- Assert.Equal (dim1, dim2);
-
- n1 = n2 = -1;
- dim1 = Dim.Sized (n1);
- dim2 = Dim.Sized (n2);
- Assert.Equal (dim1, dim2);
-
- n1 = 0;
- n2 = 1;
- dim1 = Dim.Sized (n1);
- dim2 = Dim.Sized (n2);
- Assert.NotEqual (dim1, dim2);
- }
-
- [Fact]
- public void Width_Set_To_Null_Throws ()
- {
- var dim = Dim.Width (null);
- Assert.Throws (() => dim.ToString ());
- }
-
- [Fact, TestRespondersDisposed]
- public void SetsValue ()
- {
- var testVal = Rect.Empty;
- var testValView = new View (testVal);
- var dim = Dim.Width (testValView);
- Assert.Equal ($"View(Width,View(){testVal})", dim.ToString ());
- testValView.Dispose ();
-
- testVal = new Rect (1, 2, 3, 4);
- testValView = new View (testVal);
- dim = Dim.Width (testValView);
- Assert.Equal ($"View(Width,View(){testVal})", dim.ToString ());
- testValView.Dispose ();
- }
-
- [Fact] [TestRespondersDisposed]
- public void Width_Equals ()
- {
- var testRect1 = Rect.Empty;
- var view1 = new View (testRect1);
- var testRect2 = Rect.Empty;
- var view2 = new View (testRect2);
-
- var dim1 = Dim.Width (view1);
- var dim2 = Dim.Width (view1);
- // FIXED: Dim.Width should support Equals() and this should change to Equal.
- Assert.Equal (dim1, dim2);
-
- dim2 = Dim.Width (view2);
- Assert.NotEqual (dim1, dim2);
-
- testRect1 = new Rect (0, 1, 2, 3);
- view1 = new View (testRect1);
- testRect2 = new Rect (0, 1, 2, 3);
- dim1 = Dim.Width (view1);
- dim2 = Dim.Width (view1);
- // FIXED: Dim.Width should support Equals() and this should change to Equal.
- Assert.Equal (dim1, dim2);
-
- testRect1 = new Rect (0, -1, 2, 3);
- view1 = new View (testRect1);
- testRect2 = new Rect (0, -1, 2, 3);
- dim1 = Dim.Width (view1);
- dim2 = Dim.Width (view1);
- // FIXED: Dim.Width should support Equals() and this should change to Equal.
- Assert.Equal (dim1, dim2);
-
- testRect1 = new Rect (0, -1, 2, 3);
- view1 = new View (testRect1);
- testRect2 = Rect.Empty;
- view2 = new View (testRect2);
- dim1 = Dim.Width (view1);
- dim2 = Dim.Width (view2);
- Assert.NotEqual (dim1, dim2);
-#if DEBUG_IDISPOSABLE
- // HACK: Force clean up of Responders to avoid having to Dispose all the Views created above.
- Responder.Instances.Clear ();
- Assert.Empty (Responder.Instances);
-#endif
- }
-
- [Fact]
- public void Height_Set_To_Null_Throws ()
- {
- var dim = Dim.Height (null);
- Assert.Throws (() => dim.ToString ());
- }
-
- [Fact, TestRespondersDisposed]
- public void Height_SetsValue ()
- {
- var testVal = Rect.Empty;
- var testValview = new View (testVal);
- var dim = Dim.Height (testValview);
- Assert.Equal ($"View(Height,View(){testVal})", dim.ToString ());
- testValview.Dispose ();
-
- testVal = new Rect (1, 2, 3, 4);
- testValview = new View (testVal);
- dim = Dim.Height (testValview);
- Assert.Equal ($"View(Height,View(){testVal})", dim.ToString ());
- testValview.Dispose ();
- }
-
- // TODO: Other Dim.Height tests (e.g. Equal?)
-
- [Fact]
- public void Fill_SetsValue ()
- {
- int testMargin = 0;
- var dim = Dim.Fill ();
- Assert.Equal ($"Fill({testMargin})", dim.ToString ());
-
- testMargin = 0;
- dim = Dim.Fill (testMargin);
- Assert.Equal ($"Fill({testMargin})", dim.ToString ());
-
- testMargin = 5;
- dim = Dim.Fill (testMargin);
- Assert.Equal ($"Fill({testMargin})", dim.ToString ());
- }
-
- [Fact]
- public void Fill_Equal ()
- {
- int margin1 = 0;
- int margin2 = 0;
- var dim1 = Dim.Fill (margin1);
- var dim2 = Dim.Fill (margin2);
- Assert.Equal (dim1, dim2);
- }
-
- [Fact]
- public void Percent_SetsValue ()
- {
- float f = 0;
- var dim = Dim.Percent (f);
- Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ());
- f = 0.5F;
- dim = Dim.Percent (f);
- Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ());
- f = 100;
- dim = Dim.Percent (f);
- Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ());
- }
-
- [Fact]
- public void Percent_Equals ()
- {
- float n1 = 0;
- float n2 = 0;
- var dim1 = Dim.Percent (n1);
- var dim2 = Dim.Percent (n2);
- Assert.Equal (dim1, dim2);
-
- n1 = n2 = 1;
- dim1 = Dim.Percent (n1);
- dim2 = Dim.Percent (n2);
- Assert.Equal (dim1, dim2);
-
- n1 = n2 = 0.5f;
- dim1 = Dim.Percent (n1);
- dim2 = Dim.Percent (n2);
- Assert.Equal (dim1, dim2);
-
- n1 = n2 = 100f;
- dim1 = Dim.Percent (n1);
- dim2 = Dim.Percent (n2);
- Assert.Equal (dim1, dim2);
-
- n1 = n2 = 0.3f;
- dim1 = Dim.Percent (n1, true);
- dim2 = Dim.Percent (n2, true);
- Assert.Equal (dim1, dim2);
-
- n1 = n2 = 0.3f;
- dim1 = Dim.Percent (n1);
- dim2 = Dim.Percent (n2, true);
- Assert.NotEqual (dim1, dim2);
-
- n1 = 0;
- n2 = 1;
- dim1 = Dim.Percent (n1);
- dim2 = Dim.Percent (n2);
- Assert.NotEqual (dim1, dim2);
-
- n1 = 0.5f;
- n2 = 1.5f;
- dim1 = Dim.Percent (n1);
- dim2 = Dim.Percent (n2);
- Assert.NotEqual (dim1, dim2);
- }
-
- [Fact]
- public void Percent_Invalid_Throws ()
- {
- var dim = Dim.Percent (0);
- Assert.Throws (() => dim = Dim.Percent (-1));
- Assert.Throws (() => dim = Dim.Percent (101));
- Assert.Throws (() => dim = Dim.Percent (100.0001F));
- Assert.Throws (() => dim = Dim.Percent (1000001));
- }
-
- [Fact] [AutoInitShutdown]
- public void ForceValidatePosDim_True_Dim_Validation_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_Throws ()
- {
- var t = Application.Top;
-
- var w = new Window () {
- Width = Dim.Fill (0),
- Height = Dim.Sized (10)
- };
- var v = new View ("v") {
- Width = Dim.Width (w) - 2,
- Height = Dim.Percent (10),
- ValidatePosDim = true
- };
-
- w.Add (v);
- t.Add (w);
-
- t.Ready += (s, e) => {
- Assert.Equal (2, w.Width = 2);
- Assert.Equal (2, w.Height = 2);
- Assert.Throws (() => v.Width = 2);
- Assert.Throws (() => v.Height = 2);
- v.ValidatePosDim = false;
- var exception = Record.Exception (() => v.Width = 2);
- Assert.Null (exception);
- Assert.Equal (2, v.Width);
- exception = Record.Exception (() => v.Height = 2);
- Assert.Null (exception);
- Assert.Equal (2, v.Height);
- };
-
- Application.Iteration += (s, a) => Application.RequestStop ();
-
- Application.Run ();
- }
-
- [Fact] [TestRespondersDisposed]
- public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Null ()
- {
- var t = new View ("top") { Width = 80, Height = 25 };
-
- var w = new Window (new Rect (1, 2, 4, 5)) { Title = "w" };
- t.Add (w);
- t.LayoutSubviews ();
-
- Assert.Equal (3, w.Width = 3);
- Assert.Equal (4, w.Height = 4);
- t.Dispose ();
- }
-
- [Fact] [TestRespondersDisposed]
- public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute ()
- {
- var t = new View ("top") { Width = 80, Height = 25 };
-
- var w = new Window () {
- Width = Dim.Fill (0),
- Height = Dim.Sized (10)
- };
- var v = new View ("v") {
- Width = Dim.Width (w) - 2,
- Height = Dim.Percent (10)
- };
-
- w.Add (v);
- t.Add (w);
-
- t.LayoutSubviews ();
- Assert.Equal (2, v.Width = 2);
- Assert.Equal (2, v.Height = 2);
-
- v.LayoutStyle = LayoutStyle.Absolute;
- t.LayoutSubviews ();
-
- Assert.Equal (2, v.Width = 2);
- Assert.Equal (2, v.Height = 2);
- t.Dispose ();
- }
-
- [Fact] [AutoInitShutdown]
- public void Only_DimAbsolute_And_DimFactor_As_A_Different_Procedure_For_Assigning_Value_To_Width_Or_Height ()
- {
- // Testing with the Button because it properly handles the Dim class.
- var t = Application.Top;
-
- var w = new Window () {
- Width = 100,
- Height = 100
- };
-
- var f1 = new FrameView ("f1") {
- X = 0,
- Y = 0,
- Width = Dim.Percent (50),
- Height = 5
- };
-
- var f2 = new FrameView ("f2") {
- X = Pos.Right (f1),
- Y = 0,
- Width = Dim.Fill (),
- Height = 5
- };
-
- var v1 = new Button ("v1") {
- AutoSize = false,
- X = Pos.X (f1) + 2,
- Y = Pos.Bottom (f1) + 2,
- Width = Dim.Width (f1) - 2,
- Height = Dim.Fill () - 2,
- ValidatePosDim = true
- };
-
- var v2 = new Button ("v2") {
- AutoSize = false,
- X = Pos.X (f2) + 2,
- Y = Pos.Bottom (f2) + 2,
- Width = Dim.Width (f2) - 2,
- Height = Dim.Fill () - 2,
- ValidatePosDim = true
- };
-
- var v3 = new Button ("v3") {
- AutoSize = false,
- Width = Dim.Percent (10),
- Height = Dim.Percent (10),
- ValidatePosDim = true
- };
-
- var v4 = new Button ("v4") {
- AutoSize = false,
- Width = Dim.Sized (50),
- Height = Dim.Sized (50),
- ValidatePosDim = true
- };
-
- var v5 = new Button ("v5") {
- AutoSize = false,
- Width = Dim.Width (v1) - Dim.Width (v3),
- Height = Dim.Height (v1) - Dim.Height (v3),
- ValidatePosDim = true
- };
-
- var v6 = new Button ("v6") {
- AutoSize = false,
- X = Pos.X (f2),
- Y = Pos.Bottom (f2) + 2,
- Width = Dim.Percent (20, true),
- Height = Dim.Percent (20, true),
- ValidatePosDim = true
- };
-
- w.Add (f1, f2, v1, v2, v3, v4, v5, v6);
- t.Add (w);
-
- t.Ready += (s, e) => {
- Assert.Equal ("Absolute(100)", w.Width.ToString ());
- Assert.Equal ("Absolute(100)", w.Height.ToString ());
- Assert.Equal (100, w.Frame.Width);
- Assert.Equal (100, w.Frame.Height);
-
- Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ());
- Assert.Equal ("Absolute(5)", f1.Height.ToString ());
- Assert.Equal (49, f1.Frame.Width); // 50-1=49
- Assert.Equal (5, f1.Frame.Height);
-
- Assert.Equal ("Fill(0)", f2.Width.ToString ());
- Assert.Equal ("Absolute(5)", f2.Height.ToString ());
- Assert.Equal (49, f2.Frame.Width); // 50-1=49
- Assert.Equal (5, f2.Frame.Height);
-
- Assert.Equal ("Combine(View(Width,FrameView(f1)(0,0,49,5))-Absolute(2))", v1.Width.ToString ());
- Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ());
- Assert.Equal (47, v1.Frame.Width); // 49-2=47
- Assert.Equal (89, v1.Frame.Height); // 98-5-2-2=89
-
- Assert.Equal ("Combine(View(Width,FrameView(f2)(49,0,49,5))-Absolute(2))", v2.Width.ToString ());
- Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
- Assert.Equal (47, v2.Frame.Width); // 49-2=47
- Assert.Equal (89, v2.Frame.Height); // 98-5-2-2=89
-
- Assert.Equal ("Factor(0.1,False)", v3.Width.ToString ());
- Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ());
- Assert.Equal (9, v3.Frame.Width); // 98*10%=9
- Assert.Equal (9, v3.Frame.Height); // 98*10%=9
-
- Assert.Equal ("Absolute(50)", v4.Width.ToString ());
- Assert.Equal ("Absolute(50)", v4.Height.ToString ());
- Assert.Equal (50, v4.Frame.Width);
- Assert.Equal (50, v4.Frame.Height);
-
- Assert.Equal ("Combine(View(Width,Button(v1)(2,7,47,89))-View(Width,Button(v3)(0,0,9,9)))", v5.Width.ToString ());
- Assert.Equal ("Combine(View(Height,Button(v1)(2,7,47,89))-View(Height,Button(v3)(0,0,9,9)))", v5.Height.ToString ());
- Assert.Equal (38, v5.Frame.Width); // 47-9=38
- Assert.Equal (80, v5.Frame.Height); // 89-9=80
-
- Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ());
- Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ());
- Assert.Equal (9, v6.Frame.Width); // 47*20%=9
- Assert.Equal (18, v6.Frame.Height); // 89*20%=18
-
- w.Width = 200;
- Assert.True (t.LayoutNeeded);
- w.Height = 200;
- t.LayoutSubviews ();
-
- Assert.Equal ("Absolute(200)", w.Width.ToString ());
- Assert.Equal ("Absolute(200)", w.Height.ToString ());
- Assert.Equal (200, w.Frame.Width);
- Assert.Equal (200, w.Frame.Height);
-
- f1.Text = "Frame1";
- Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ());
- Assert.Equal ("Absolute(5)", f1.Height.ToString ());
- Assert.Equal (99, f1.Frame.Width); // 100-1=99
- Assert.Equal (5, f1.Frame.Height);
-
- f2.Text = "Frame2";
- Assert.Equal ("Fill(0)", f2.Width.ToString ());
- Assert.Equal ("Absolute(5)", f2.Height.ToString ());
- Assert.Equal (99, f2.Frame.Width); // 100-1=99
- Assert.Equal (5, f2.Frame.Height);
-
- v1.Text = "Button1";
- Assert.Equal ("Combine(View(Width,FrameView(f1)(0,0,99,5))-Absolute(2))", v1.Width.ToString ());
- Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ());
- Assert.Equal (97, v1.Frame.Width); // 99-2=97
- Assert.Equal (189, v1.Frame.Height); // 198-2-7=189
-
- v2.Text = "Button2";
- Assert.Equal ("Combine(View(Width,FrameView(f2)(99,0,99,5))-Absolute(2))", v2.Width.ToString ());
- Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
- Assert.Equal (97, v2.Frame.Width); // 99-2=97
- Assert.Equal (189, v2.Frame.Height); // 198-2-7=189
-
- v3.Text = "Button3";
- Assert.Equal ("Factor(0.1,False)", v3.Width.ToString ());
- Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ());
- Assert.Equal (19, v3.Frame.Width); // 198*10%=19 * Percent is related to the super-view if it isn't null otherwise the view width
- Assert.Equal (19, v3.Frame.Height); // 199*10%=19
-
- v4.Text = "Button4";
- v4.AutoSize = false;
- Assert.Equal ("Absolute(50)", v4.Width.ToString ());
- Assert.Equal ("Absolute(50)", v4.Height.ToString ());
- Assert.Equal (50, v4.Frame.Width);
- Assert.Equal (50, v4.Frame.Height);
- v4.AutoSize = true;
- Assert.Equal ("Absolute(11)", v4.Width.ToString ());
- Assert.Equal ("Absolute(1)", v4.Height.ToString ());
- Assert.Equal (11, v4.Frame.Width); // 11 is the text length and because is Dim.DimAbsolute
- Assert.Equal (1, v4.Frame.Height); // 1 because is Dim.DimAbsolute
-
- v5.Text = "Button5";
- Assert.Equal ("Combine(View(Width,Button(v1)(2,7,97,189))-View(Width,Button(v3)(0,0,19,19)))", v5.Width.ToString ());
- Assert.Equal ("Combine(View(Height,Button(v1)(2,7,97,189))-View(Height,Button(v3)(0,0,19,19)))", v5.Height.ToString ());
- Assert.Equal (78, v5.Frame.Width); // 97-9=78
- Assert.Equal (170, v5.Frame.Height); // 189-19=170
-
- v6.Text = "Button6";
- Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ());
- Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ());
- Assert.Equal (19, v6.Frame.Width); // 99*20%=19
- Assert.Equal (38, v6.Frame.Height); // 198-7*20=18
- };
-
- Application.Iteration += (s, a) => Application.RequestStop ();
-
- Application.Run ();
- }
-
- // See #2461
- //[Fact]
- //public void Dim_Referencing_SuperView_Throws ()
- //{
- // var super = new View ("super") {
- // Width = 10,
- // Height = 10
- // };
- // var view = new View ("view") {
- // Width = Dim.Width (super), // this is not allowed
- // Height = Dim.Height (super), // this is not allowed
- // };
-
- // super.Add (view);
- // super.BeginInit ();
- // super.EndInit ();
- // Assert.Throws (() => super.LayoutSubviews ());
- //}
-
- ///
- /// This is an intentionally obtuse test. See https://github.com/gui-cs/Terminal.Gui/issues/2461
- ///
- [Fact] [TestRespondersDisposed]
- public void DimCombine_ObtuseScenario_Throw_If_SuperView_Refs_SubView ()
- {
- var t = new View () { Width = 80, Height = 25 };
-
- var w = new Window () {
- Width = Dim.Width (t) - 2, // 78
- Height = Dim.Height (t) - 2 // 23
- };
- var f = new FrameView ();
- var v1 = new View () {
- Width = Dim.Width (w) - 2, // 76
- Height = Dim.Height (w) - 2 // 21
- };
- var v2 = new View () {
- Width = Dim.Width (v1) - 2, // 74
- Height = Dim.Height (v1) - 2 // 19
- };
-
- f.Add (v1, v2);
- w.Add (f);
- t.Add (w);
- t.BeginInit ();
- t.EndInit ();
-
- // BUGBUG: v2 - f references t here; t is f's super-superview. This is supported!
- // BUGBUG: v2 - f references v2 here; v2 is f's subview. This is not supported!
- f.Width = Dim.Width (t) - Dim.Width (v2); // 80 - 74 = 6
- f.Height = Dim.Height (t) - Dim.Height (v2); // 25 - 19 = 6
-
- Assert.Throws (t.LayoutSubviews);
- Assert.Equal (80, t.Frame.Width);
- Assert.Equal (25, t.Frame.Height);
- Assert.Equal (78, w.Frame.Width);
- Assert.Equal (23, w.Frame.Height);
- // BUGBUG: v2 - this no longer works - see above
- //Assert.Equal (6, f.Frame.Width);
- //Assert.Equal (6, f.Frame.Height);
- //Assert.Equal (76, v1.Frame.Width);
- //Assert.Equal (21, v1.Frame.Height);
- //Assert.Equal (74, v2.Frame.Width);
- //Assert.Equal (19, v2.Frame.Height);
- t.Dispose ();
- }
-
- [Fact] [TestRespondersDisposed]
- public void DimCombine_ObtuseScenario_Does_Not_Throw_If_Two_SubViews_Refs_The_Same_SuperView ()
- {
- var t = new View ("top") { Width = 80, Height = 25 };
-
- var w = new Window () {
- Width = Dim.Width (t) - 2, // 78
- Height = Dim.Height (t) - 2 // 23
- };
- var f = new FrameView ();
- var v1 = new View () {
- Width = Dim.Width (w) - 2, // 76
- Height = Dim.Height (w) - 2 // 21
- };
- var v2 = new View () {
- Width = Dim.Width (v1) - 2, // 74
- Height = Dim.Height (v1) - 2 // 19
- };
-
- f.Add (v1, v2);
- w.Add (f);
- t.Add (w);
- t.BeginInit ();
- t.EndInit ();
-
- f.Width = Dim.Width (t) - Dim.Width (w) + 4; // 80 - 74 = 6
- f.Height = Dim.Height (t) - Dim.Height (w) + 4; // 25 - 19 = 6
-
- // BUGBUG: v2 - f references t and w here; t is f's super-superview and w is f's superview. This is supported!
- var exception = Record.Exception (t.LayoutSubviews);
- Assert.Null (exception);
- Assert.Equal (80, t.Frame.Width);
- Assert.Equal (25, t.Frame.Height);
- Assert.Equal (78, w.Frame.Width);
- Assert.Equal (23, w.Frame.Height);
- Assert.Equal (6, f.Frame.Width);
- Assert.Equal (6, f.Frame.Height);
- Assert.Equal (76, v1.Frame.Width);
- Assert.Equal (21, v1.Frame.Height);
- Assert.Equal (74, v2.Frame.Width);
- Assert.Equal (19, v2.Frame.Height);
- t.Dispose ();
- }
-
- [Fact] [TestRespondersDisposed]
- public void PosCombine_View_Not_Added_Throws ()
- {
- var t = new View () { Width = 80, Height = 50 };
-
- // BUGBUG: v2 - super should not reference it's superview (t)
- var super = new View () {
- Width = Dim.Width (t) - 2,
- Height = Dim.Height (t) - 2
- };
- t.Add (super);
-
- var sub = new View ();
- super.Add (sub);
-
- var v1 = new View () {
- Width = Dim.Width (super) - 2,
- Height = Dim.Height (super) - 2
- };
- var v2 = new View () {
- Width = Dim.Width (v1) - 2,
- Height = Dim.Height (v1) - 2
- };
- sub.Add (v1);
- // v2 not added to sub; should cause exception on Layout since it's referenced by sub.
- sub.Width = Dim.Fill () - Dim.Width (v2);
- sub.Height = Dim.Fill () - Dim.Height (v2);
-
- t.BeginInit ();
- t.EndInit ();
-
- Assert.Throws (() => t.LayoutSubviews ());
- t.Dispose ();
- v2.Dispose ();
- }
-
- [Fact] [AutoInitShutdown]
- public void Dim_Add_Operator ()
- {
- var top = Application.Top;
-
- var view = new View () { X = 0, Y = 0, Width = 20, Height = 0 };
- var field = new TextField () { X = 0, Y = Pos.Bottom (view), Width = 20 };
- int count = 0;
-
- field.KeyDown += (s, k) => {
- if (k.KeyCode == KeyCode.Enter) {
- field.Text = $"Label {count}";
- var label = new Label (field.Text) { X = 0, Y = view.Bounds.Height, Width = 20 };
- view.Add (label);
- Assert.Equal ($"Label {count}", label.Text);
- Assert.Equal ($"Absolute({count})", label.Y.ToString ());
-
- Assert.Equal ($"Absolute({count})", view.Height.ToString ());
- view.Height += 1;
- count++;
- Assert.Equal ($"Absolute({count})", view.Height.ToString ());
- }
- };
-
- Application.Iteration += (s, a) => {
- while (count < 20) {
- field.NewKeyDownEvent (new Key (KeyCode.Enter));
- }
-
- Application.RequestStop ();
- };
-
- var win = new Window ();
- win.Add (view);
- win.Add (field);
-
- top.Add (win);
-
- Application.Run (top);
-
- Assert.Equal (20, count);
- }
-
- string [] expecteds = new string [21] {
+ readonly string [] expecteds = new string [21] {
@"
┌────────────────────┐
│View with long text │
@@ -1042,6 +331,717 @@ public class DimTests {
└────────────────────┘"
};
+ public DimTests (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;
+ }
+
+ [Fact]
+ public void New_Works ()
+ {
+ var dim = new Dim ();
+ Assert.Equal ("Terminal.Gui.Dim", dim.ToString ());
+ }
+
+ [Fact]
+ public void Sized_SetsValue ()
+ {
+ var dim = Dim.Sized (0);
+ Assert.Equal ("Absolute(0)", dim.ToString ());
+
+ var testVal = 5;
+ dim = Dim.Sized (testVal);
+ Assert.Equal ($"Absolute({testVal})", dim.ToString ());
+
+ testVal = -1;
+ dim = Dim.Sized (testVal);
+ Assert.Equal ($"Absolute({testVal})", dim.ToString ());
+ }
+
+ [Fact]
+ public void Sized_Equals ()
+ {
+ var n1 = 0;
+ var n2 = 0;
+ var dim1 = Dim.Sized (n1);
+ var dim2 = Dim.Sized (n2);
+ Assert.Equal (dim1, dim2);
+
+ n1 = n2 = 1;
+ dim1 = Dim.Sized (n1);
+ dim2 = Dim.Sized (n2);
+ Assert.Equal (dim1, dim2);
+
+ n1 = n2 = -1;
+ dim1 = Dim.Sized (n1);
+ dim2 = Dim.Sized (n2);
+ Assert.Equal (dim1, dim2);
+
+ n1 = 0;
+ n2 = 1;
+ dim1 = Dim.Sized (n1);
+ dim2 = Dim.Sized (n2);
+ Assert.NotEqual (dim1, dim2);
+ }
+
+ [Fact]
+ public void Width_Set_To_Null_Throws ()
+ {
+ var dim = Dim.Width (null);
+ Assert.Throws (() => dim.ToString ());
+ }
+
+ [Fact] [TestRespondersDisposed]
+ public void SetsValue ()
+ {
+ var testVal = Rect.Empty;
+ var testValView = new View (testVal);
+ var dim = Dim.Width (testValView);
+ Assert.Equal ($"View(Width,View(){testVal})", dim.ToString ());
+ testValView.Dispose ();
+
+ testVal = new Rect (1, 2, 3, 4);
+ testValView = new View (testVal);
+ dim = Dim.Width (testValView);
+ Assert.Equal ($"View(Width,View(){testVal})", dim.ToString ());
+ testValView.Dispose ();
+ }
+
+ [Fact] [TestRespondersDisposed]
+ public void Width_Equals ()
+ {
+ var testRect1 = Rect.Empty;
+ var view1 = new View (testRect1);
+ var testRect2 = Rect.Empty;
+ var view2 = new View (testRect2);
+
+ var dim1 = Dim.Width (view1);
+ var dim2 = Dim.Width (view1);
+ // FIXED: Dim.Width should support Equals() and this should change to Equal.
+ Assert.Equal (dim1, dim2);
+
+ dim2 = Dim.Width (view2);
+ Assert.NotEqual (dim1, dim2);
+
+ testRect1 = new Rect (0, 1, 2, 3);
+ view1 = new View (testRect1);
+ testRect2 = new Rect (0, 1, 2, 3);
+ dim1 = Dim.Width (view1);
+ dim2 = Dim.Width (view1);
+ // FIXED: Dim.Width should support Equals() and this should change to Equal.
+ Assert.Equal (dim1, dim2);
+
+ testRect1 = new Rect (0, -1, 2, 3);
+ view1 = new View (testRect1);
+ testRect2 = new Rect (0, -1, 2, 3);
+ dim1 = Dim.Width (view1);
+ dim2 = Dim.Width (view1);
+ // FIXED: Dim.Width should support Equals() and this should change to Equal.
+ Assert.Equal (dim1, dim2);
+
+ testRect1 = new Rect (0, -1, 2, 3);
+ view1 = new View (testRect1);
+ testRect2 = Rect.Empty;
+ view2 = new View (testRect2);
+ dim1 = Dim.Width (view1);
+ dim2 = Dim.Width (view2);
+ Assert.NotEqual (dim1, dim2);
+#if DEBUG_IDISPOSABLE
+ // HACK: Force clean up of Responders to avoid having to Dispose all the Views created above.
+ Responder.Instances.Clear ();
+ Assert.Empty (Responder.Instances);
+#endif
+ }
+
+ [Fact]
+ public void Height_Set_To_Null_Throws ()
+ {
+ var dim = Dim.Height (null);
+ Assert.Throws (() => dim.ToString ());
+ }
+
+ [Fact] [TestRespondersDisposed]
+ public void Height_SetsValue ()
+ {
+ var testVal = Rect.Empty;
+ var testValview = new View (testVal);
+ var dim = Dim.Height (testValview);
+ Assert.Equal ($"View(Height,View(){testVal})", dim.ToString ());
+ testValview.Dispose ();
+
+ testVal = new Rect (1, 2, 3, 4);
+ testValview = new View (testVal);
+ dim = Dim.Height (testValview);
+ Assert.Equal ($"View(Height,View(){testVal})", dim.ToString ());
+ testValview.Dispose ();
+ }
+
+ // TODO: Other Dim.Height tests (e.g. Equal?)
+
+ [Fact]
+ public void Fill_SetsValue ()
+ {
+ var testMargin = 0;
+ var dim = Dim.Fill ();
+ Assert.Equal ($"Fill({testMargin})", dim.ToString ());
+
+ testMargin = 0;
+ dim = Dim.Fill (testMargin);
+ Assert.Equal ($"Fill({testMargin})", dim.ToString ());
+
+ testMargin = 5;
+ dim = Dim.Fill (testMargin);
+ Assert.Equal ($"Fill({testMargin})", dim.ToString ());
+ }
+
+ [Fact]
+ public void Fill_Equal ()
+ {
+ var margin1 = 0;
+ var margin2 = 0;
+ var dim1 = Dim.Fill (margin1);
+ var dim2 = Dim.Fill (margin2);
+ Assert.Equal (dim1, dim2);
+ }
+
+ [Fact]
+ public void Percent_SetsValue ()
+ {
+ float f = 0;
+ var dim = Dim.Percent (f);
+ Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ());
+ f = 0.5F;
+ dim = Dim.Percent (f);
+ Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ());
+ f = 100;
+ dim = Dim.Percent (f);
+ Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ());
+ }
+
+ [Fact]
+ public void Percent_Equals ()
+ {
+ float n1 = 0;
+ float n2 = 0;
+ var dim1 = Dim.Percent (n1);
+ var dim2 = Dim.Percent (n2);
+ Assert.Equal (dim1, dim2);
+
+ n1 = n2 = 1;
+ dim1 = Dim.Percent (n1);
+ dim2 = Dim.Percent (n2);
+ Assert.Equal (dim1, dim2);
+
+ n1 = n2 = 0.5f;
+ dim1 = Dim.Percent (n1);
+ dim2 = Dim.Percent (n2);
+ Assert.Equal (dim1, dim2);
+
+ n1 = n2 = 100f;
+ dim1 = Dim.Percent (n1);
+ dim2 = Dim.Percent (n2);
+ Assert.Equal (dim1, dim2);
+
+ n1 = n2 = 0.3f;
+ dim1 = Dim.Percent (n1, true);
+ dim2 = Dim.Percent (n2, true);
+ Assert.Equal (dim1, dim2);
+
+ n1 = n2 = 0.3f;
+ dim1 = Dim.Percent (n1);
+ dim2 = Dim.Percent (n2, true);
+ Assert.NotEqual (dim1, dim2);
+
+ n1 = 0;
+ n2 = 1;
+ dim1 = Dim.Percent (n1);
+ dim2 = Dim.Percent (n2);
+ Assert.NotEqual (dim1, dim2);
+
+ n1 = 0.5f;
+ n2 = 1.5f;
+ dim1 = Dim.Percent (n1);
+ dim2 = Dim.Percent (n2);
+ Assert.NotEqual (dim1, dim2);
+ }
+
+ [Fact]
+ public void Percent_Invalid_Throws ()
+ {
+ var dim = Dim.Percent (0);
+ Assert.Throws (() => dim = Dim.Percent (-1));
+ Assert.Throws (() => dim = Dim.Percent (101));
+ Assert.Throws (() => dim = Dim.Percent (100.0001F));
+ Assert.Throws (() => dim = Dim.Percent (1000001));
+ }
+
+ [Fact] [AutoInitShutdown]
+ public void ForceValidatePosDim_True_Dim_Validation_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_Throws ()
+ {
+ var t = Application.Top;
+
+ var w = new Window {
+ Width = Dim.Fill (),
+ Height = Dim.Sized (10)
+ };
+ var v = new View ("v") {
+ Width = Dim.Width (w) - 2,
+ Height = Dim.Percent (10),
+ ValidatePosDim = true
+ };
+
+ w.Add (v);
+ t.Add (w);
+
+ t.Ready += (s, e) => {
+ Assert.Equal (2, w.Width = 2);
+ Assert.Equal (2, w.Height = 2);
+ Assert.Throws (() => v.Width = 2);
+ Assert.Throws (() => v.Height = 2);
+ v.ValidatePosDim = false;
+ var exception = Record.Exception (() => v.Width = 2);
+ Assert.Null (exception);
+ Assert.Equal (2, v.Width);
+ exception = Record.Exception (() => v.Height = 2);
+ Assert.Null (exception);
+ Assert.Equal (2, v.Height);
+ };
+
+ Application.Iteration += (s, a) => Application.RequestStop ();
+
+ Application.Run ();
+ }
+
+ [Fact] [TestRespondersDisposed]
+ public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Null ()
+ {
+ var t = new View ("top") { Width = 80, Height = 25 };
+
+ var w = new Window (new Rect (1, 2, 4, 5)) { Title = "w" };
+ t.Add (w);
+ t.LayoutSubviews ();
+
+ Assert.Equal (3, w.Width = 3);
+ Assert.Equal (4, w.Height = 4);
+ t.Dispose ();
+ }
+
+ [Fact] [TestRespondersDisposed]
+ public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute ()
+ {
+ var t = new View ("top") { Width = 80, Height = 25 };
+
+ var w = new Window {
+ Width = Dim.Fill (),
+ Height = Dim.Sized (10)
+ };
+ var v = new View ("v") {
+ Width = Dim.Width (w) - 2,
+ Height = Dim.Percent (10)
+ };
+
+ w.Add (v);
+ t.Add (w);
+
+ t.LayoutSubviews ();
+ Assert.Equal (2, v.Width = 2);
+ Assert.Equal (2, v.Height = 2);
+
+ v.LayoutStyle = LayoutStyle.Absolute;
+ t.LayoutSubviews ();
+
+ Assert.Equal (2, v.Width = 2);
+ Assert.Equal (2, v.Height = 2);
+ t.Dispose ();
+ }
+
+ [Fact] [AutoInitShutdown]
+ public void Only_DimAbsolute_And_DimFactor_As_A_Different_Procedure_For_Assigning_Value_To_Width_Or_Height ()
+ {
+ // Testing with the Button because it properly handles the Dim class.
+ var t = Application.Top;
+
+ var w = new Window {
+ Width = 100,
+ Height = 100
+ };
+
+ var f1 = new FrameView ("f1") {
+ X = 0,
+ Y = 0,
+ Width = Dim.Percent (50),
+ Height = 5
+ };
+
+ var f2 = new FrameView ("f2") {
+ X = Pos.Right (f1),
+ Y = 0,
+ Width = Dim.Fill (),
+ Height = 5
+ };
+
+ var v1 = new Button ("v1") {
+ AutoSize = false,
+ X = Pos.X (f1) + 2,
+ Y = Pos.Bottom (f1) + 2,
+ Width = Dim.Width (f1) - 2,
+ Height = Dim.Fill () - 2,
+ ValidatePosDim = true
+ };
+
+ var v2 = new Button ("v2") {
+ AutoSize = false,
+ X = Pos.X (f2) + 2,
+ Y = Pos.Bottom (f2) + 2,
+ Width = Dim.Width (f2) - 2,
+ Height = Dim.Fill () - 2,
+ ValidatePosDim = true
+ };
+
+ var v3 = new Button ("v3") {
+ AutoSize = false,
+ Width = Dim.Percent (10),
+ Height = Dim.Percent (10),
+ ValidatePosDim = true
+ };
+
+ var v4 = new Button ("v4") {
+ AutoSize = false,
+ Width = Dim.Sized (50),
+ Height = Dim.Sized (50),
+ ValidatePosDim = true
+ };
+
+ var v5 = new Button ("v5") {
+ AutoSize = false,
+ Width = Dim.Width (v1) - Dim.Width (v3),
+ Height = Dim.Height (v1) - Dim.Height (v3),
+ ValidatePosDim = true
+ };
+
+ var v6 = new Button ("v6") {
+ AutoSize = false,
+ X = Pos.X (f2),
+ Y = Pos.Bottom (f2) + 2,
+ Width = Dim.Percent (20, true),
+ Height = Dim.Percent (20, true),
+ ValidatePosDim = true
+ };
+
+ w.Add (f1, f2, v1, v2, v3, v4, v5, v6);
+ t.Add (w);
+
+ t.Ready += (s, e) => {
+ Assert.Equal ("Absolute(100)", w.Width.ToString ());
+ Assert.Equal ("Absolute(100)", w.Height.ToString ());
+ Assert.Equal (100, w.Frame.Width);
+ Assert.Equal (100, w.Frame.Height);
+
+ Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ());
+ Assert.Equal ("Absolute(5)", f1.Height.ToString ());
+ Assert.Equal (49, f1.Frame.Width); // 50-1=49
+ Assert.Equal (5, f1.Frame.Height);
+
+ Assert.Equal ("Fill(0)", f2.Width.ToString ());
+ Assert.Equal ("Absolute(5)", f2.Height.ToString ());
+ Assert.Equal (49, f2.Frame.Width); // 50-1=49
+ Assert.Equal (5, f2.Frame.Height);
+
+ Assert.Equal ("Combine(View(Width,FrameView(f1)(0,0,49,5))-Absolute(2))", v1.Width.ToString ());
+ Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ());
+ Assert.Equal (47, v1.Frame.Width); // 49-2=47
+ Assert.Equal (89, v1.Frame.Height); // 98-5-2-2=89
+
+ Assert.Equal ("Combine(View(Width,FrameView(f2)(49,0,49,5))-Absolute(2))", v2.Width.ToString ());
+ Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
+ Assert.Equal (47, v2.Frame.Width); // 49-2=47
+ Assert.Equal (89, v2.Frame.Height); // 98-5-2-2=89
+
+ Assert.Equal ("Factor(0.1,False)", v3.Width.ToString ());
+ Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ());
+ Assert.Equal (9, v3.Frame.Width); // 98*10%=9
+ Assert.Equal (9, v3.Frame.Height); // 98*10%=9
+
+ Assert.Equal ("Absolute(50)", v4.Width.ToString ());
+ Assert.Equal ("Absolute(50)", v4.Height.ToString ());
+ Assert.Equal (50, v4.Frame.Width);
+ Assert.Equal (50, v4.Frame.Height);
+
+ Assert.Equal ("Combine(View(Width,Button(v1)(2,7,47,89))-View(Width,Button(v3)(0,0,9,9)))", v5.Width.ToString ());
+ Assert.Equal ("Combine(View(Height,Button(v1)(2,7,47,89))-View(Height,Button(v3)(0,0,9,9)))", v5.Height.ToString ());
+ Assert.Equal (38, v5.Frame.Width); // 47-9=38
+ Assert.Equal (80, v5.Frame.Height); // 89-9=80
+
+ Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ());
+ Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ());
+ Assert.Equal (9, v6.Frame.Width); // 47*20%=9
+ Assert.Equal (18, v6.Frame.Height); // 89*20%=18
+
+ w.Width = 200;
+ Assert.True (t.LayoutNeeded);
+ w.Height = 200;
+ t.LayoutSubviews ();
+
+ Assert.Equal ("Absolute(200)", w.Width.ToString ());
+ Assert.Equal ("Absolute(200)", w.Height.ToString ());
+ Assert.Equal (200, w.Frame.Width);
+ Assert.Equal (200, w.Frame.Height);
+
+ f1.Text = "Frame1";
+ Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ());
+ Assert.Equal ("Absolute(5)", f1.Height.ToString ());
+ Assert.Equal (99, f1.Frame.Width); // 100-1=99
+ Assert.Equal (5, f1.Frame.Height);
+
+ f2.Text = "Frame2";
+ Assert.Equal ("Fill(0)", f2.Width.ToString ());
+ Assert.Equal ("Absolute(5)", f2.Height.ToString ());
+ Assert.Equal (99, f2.Frame.Width); // 100-1=99
+ Assert.Equal (5, f2.Frame.Height);
+
+ v1.Text = "Button1";
+ Assert.Equal ("Combine(View(Width,FrameView(f1)(0,0,99,5))-Absolute(2))", v1.Width.ToString ());
+ Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ());
+ Assert.Equal (97, v1.Frame.Width); // 99-2=97
+ Assert.Equal (189, v1.Frame.Height); // 198-2-7=189
+
+ v2.Text = "Button2";
+ Assert.Equal ("Combine(View(Width,FrameView(f2)(99,0,99,5))-Absolute(2))", v2.Width.ToString ());
+ Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
+ Assert.Equal (97, v2.Frame.Width); // 99-2=97
+ Assert.Equal (189, v2.Frame.Height); // 198-2-7=189
+
+ v3.Text = "Button3";
+ Assert.Equal ("Factor(0.1,False)", v3.Width.ToString ());
+ Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ());
+ Assert.Equal (19, v3.Frame.Width); // 198*10%=19 * Percent is related to the super-view if it isn't null otherwise the view width
+ Assert.Equal (19, v3.Frame.Height); // 199*10%=19
+
+ v4.Text = "Button4";
+ v4.AutoSize = false;
+ Assert.Equal ("Absolute(50)", v4.Width.ToString ());
+ Assert.Equal ("Absolute(50)", v4.Height.ToString ());
+ Assert.Equal (50, v4.Frame.Width);
+ Assert.Equal (50, v4.Frame.Height);
+ v4.AutoSize = true;
+ Assert.Equal ("Absolute(11)", v4.Width.ToString ());
+ Assert.Equal ("Absolute(1)", v4.Height.ToString ());
+ Assert.Equal (11, v4.Frame.Width); // 11 is the text length and because is Dim.DimAbsolute
+ Assert.Equal (1, v4.Frame.Height); // 1 because is Dim.DimAbsolute
+
+ v5.Text = "Button5";
+ Assert.Equal ("Combine(View(Width,Button(v1)(2,7,97,189))-View(Width,Button(v3)(0,0,19,19)))", v5.Width.ToString ());
+ Assert.Equal ("Combine(View(Height,Button(v1)(2,7,97,189))-View(Height,Button(v3)(0,0,19,19)))", v5.Height.ToString ());
+ Assert.Equal (78, v5.Frame.Width); // 97-9=78
+ Assert.Equal (170, v5.Frame.Height); // 189-19=170
+
+ v6.Text = "Button6";
+ Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ());
+ Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ());
+ Assert.Equal (19, v6.Frame.Width); // 99*20%=19
+ Assert.Equal (38, v6.Frame.Height); // 198-7*20=18
+ };
+
+ Application.Iteration += (s, a) => Application.RequestStop ();
+
+ Application.Run ();
+ }
+
+ // See #2461
+ //[Fact]
+ //public void Dim_Referencing_SuperView_Throws ()
+ //{
+ // var super = new View ("super") {
+ // Width = 10,
+ // Height = 10
+ // };
+ // var view = new View ("view") {
+ // Width = Dim.Width (super), // this is not allowed
+ // Height = Dim.Height (super), // this is not allowed
+ // };
+
+ // super.Add (view);
+ // super.BeginInit ();
+ // super.EndInit ();
+ // Assert.Throws (() => super.LayoutSubviews ());
+ //}
+
+ ///
+ /// This is an intentionally obtuse test. See https://github.com/gui-cs/Terminal.Gui/issues/2461
+ ///
+ [Fact] [TestRespondersDisposed]
+ public void DimCombine_ObtuseScenario_Throw_If_SuperView_Refs_SubView ()
+ {
+ var t = new View { Width = 80, Height = 25 };
+
+ var w = new Window {
+ Width = Dim.Width (t) - 2, // 78
+ Height = Dim.Height (t) - 2 // 23
+ };
+ var f = new FrameView ();
+ var v1 = new View {
+ Width = Dim.Width (w) - 2, // 76
+ Height = Dim.Height (w) - 2 // 21
+ };
+ var v2 = new View {
+ Width = Dim.Width (v1) - 2, // 74
+ Height = Dim.Height (v1) - 2 // 19
+ };
+
+ f.Add (v1, v2);
+ w.Add (f);
+ t.Add (w);
+ t.BeginInit ();
+ t.EndInit ();
+
+ // BUGBUG: v2 - f references t here; t is f's super-superview. This is supported!
+ // BUGBUG: v2 - f references v2 here; v2 is f's subview. This is not supported!
+ f.Width = Dim.Width (t) - Dim.Width (v2); // 80 - 74 = 6
+ f.Height = Dim.Height (t) - Dim.Height (v2); // 25 - 19 = 6
+
+ Assert.Throws (t.LayoutSubviews);
+ Assert.Equal (80, t.Frame.Width);
+ Assert.Equal (25, t.Frame.Height);
+ Assert.Equal (78, w.Frame.Width);
+ Assert.Equal (23, w.Frame.Height);
+ // BUGBUG: v2 - this no longer works - see above
+ //Assert.Equal (6, f.Frame.Width);
+ //Assert.Equal (6, f.Frame.Height);
+ //Assert.Equal (76, v1.Frame.Width);
+ //Assert.Equal (21, v1.Frame.Height);
+ //Assert.Equal (74, v2.Frame.Width);
+ //Assert.Equal (19, v2.Frame.Height);
+ t.Dispose ();
+ }
+
+ [Fact] [TestRespondersDisposed]
+ public void DimCombine_ObtuseScenario_Does_Not_Throw_If_Two_SubViews_Refs_The_Same_SuperView ()
+ {
+ var t = new View ("top") { Width = 80, Height = 25 };
+
+ var w = new Window {
+ Width = Dim.Width (t) - 2, // 78
+ Height = Dim.Height (t) - 2 // 23
+ };
+ var f = new FrameView ();
+ var v1 = new View {
+ Width = Dim.Width (w) - 2, // 76
+ Height = Dim.Height (w) - 2 // 21
+ };
+ var v2 = new View {
+ Width = Dim.Width (v1) - 2, // 74
+ Height = Dim.Height (v1) - 2 // 19
+ };
+
+ f.Add (v1, v2);
+ w.Add (f);
+ t.Add (w);
+ t.BeginInit ();
+ t.EndInit ();
+
+ f.Width = Dim.Width (t) - Dim.Width (w) + 4; // 80 - 74 = 6
+ f.Height = Dim.Height (t) - Dim.Height (w) + 4; // 25 - 19 = 6
+
+ // BUGBUG: v2 - f references t and w here; t is f's super-superview and w is f's superview. This is supported!
+ var exception = Record.Exception (t.LayoutSubviews);
+ Assert.Null (exception);
+ Assert.Equal (80, t.Frame.Width);
+ Assert.Equal (25, t.Frame.Height);
+ Assert.Equal (78, w.Frame.Width);
+ Assert.Equal (23, w.Frame.Height);
+ Assert.Equal (6, f.Frame.Width);
+ Assert.Equal (6, f.Frame.Height);
+ Assert.Equal (76, v1.Frame.Width);
+ Assert.Equal (21, v1.Frame.Height);
+ Assert.Equal (74, v2.Frame.Width);
+ Assert.Equal (19, v2.Frame.Height);
+ t.Dispose ();
+ }
+
+ [Fact] [TestRespondersDisposed]
+ public void PosCombine_View_Not_Added_Throws ()
+ {
+ var t = new View { Width = 80, Height = 50 };
+
+ // BUGBUG: v2 - super should not reference it's superview (t)
+ var super = new View {
+ Width = Dim.Width (t) - 2,
+ Height = Dim.Height (t) - 2
+ };
+ t.Add (super);
+
+ var sub = new View ();
+ super.Add (sub);
+
+ var v1 = new View {
+ Width = Dim.Width (super) - 2,
+ Height = Dim.Height (super) - 2
+ };
+ var v2 = new View {
+ Width = Dim.Width (v1) - 2,
+ Height = Dim.Height (v1) - 2
+ };
+ sub.Add (v1);
+ // v2 not added to sub; should cause exception on Layout since it's referenced by sub.
+ sub.Width = Dim.Fill () - Dim.Width (v2);
+ sub.Height = Dim.Fill () - Dim.Height (v2);
+
+ t.BeginInit ();
+ t.EndInit ();
+
+ Assert.Throws (() => t.LayoutSubviews ());
+ t.Dispose ();
+ v2.Dispose ();
+ }
+
+ [Fact] [AutoInitShutdown]
+ public void Dim_Add_Operator ()
+ {
+ var top = Application.Top;
+
+ 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 = 0;
+
+ field.KeyDown += (s, k) => {
+ if (k.KeyCode == KeyCode.Enter) {
+ field.Text = $"Label {count}";
+ var label = new Label (field.Text) { X = 0, Y = view.Bounds.Height, Width = 20 };
+ view.Add (label);
+ Assert.Equal ($"Label {count}", label.Text);
+ Assert.Equal ($"Absolute({count})", label.Y.ToString ());
+
+ Assert.Equal ($"Absolute({count})", view.Height.ToString ());
+ view.Height += 1;
+ count++;
+ Assert.Equal ($"Absolute({count})", view.Height.ToString ());
+ }
+ };
+
+ Application.Iteration += (s, a) => {
+ while (count < 20) {
+ field.NewKeyDownEvent (new Key (KeyCode.Enter));
+ }
+
+ Application.RequestStop ();
+ };
+
+ var win = new Window ();
+ win.Add (view);
+ win.Add (field);
+
+ top.Add (win);
+
+ Application.Run (top);
+
+ Assert.Equal (20, count);
+ }
+
[Fact] [AutoInitShutdown]
public void Dim_Add_Operator_With_Text ()
{
@@ -1050,8 +1050,8 @@ public class DimTests {
// BUGBUG: v2 - If a View's height is zero, it should not be drawn.
//// Although view height is zero the text it's draw due the SetMinWidthHeight method
var view = new View ("View with long text") { X = 0, Y = 0, Width = 20, Height = 1 };
- var field = new TextField () { X = 0, Y = Pos.Bottom (view), Width = 20 };
- int count = 0;
+ var field = new TextField { X = 0, Y = Pos.Bottom (view), Width = 20 };
+ var count = 0;
var listLabels = new List