Merge pull request #3410 from tig/v2_481-AnchorEnd

Fixes #481. Adds `Pos.AnchorEnd ()` (no param) to automatically account for the view's dimension.
This commit is contained in:
Tig
2024-04-17 13:07:42 -06:00
committed by GitHub
28 changed files with 1169 additions and 899 deletions

View File

@@ -1,4 +1,6 @@
namespace Terminal.Gui;
using static Terminal.Gui.Dialog;
namespace Terminal.Gui;
/// <summary>
/// Describes the position of a <see cref="View"/> which can be an absolute value, a percentage, centered, or
@@ -43,7 +45,7 @@
/// </item>
/// <item>
/// <term>
/// <see cref="Pos.Anchor(int)"/>
/// <see cref="Pos.AnchorEnd()"/>
/// </term>
/// <description>
/// Creates a <see cref="Pos"/> object that is anchored to the end (right side or bottom) of
@@ -125,20 +127,36 @@
public class Pos
{
/// <summary>
/// Creates a <see cref="Pos"/> 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 <see cref="Pos"/> object that is anchored to the end (right side or
/// bottom) of the SuperView, minus the respective dimension of the View. This is equivalent to using
/// <see cref="Pos.AnchorEnd(int)"/>,
/// with an offset equivalent to the View's respective dimension.
/// </summary>
/// <returns>The <see cref="Pos"/> object anchored to the end (the bottom or the right side) minus the View's dimension.</returns>
/// <example>
/// This sample shows how align a <see cref="Button"/> to the bottom-right the SuperView.
/// <code>
/// anchorButton.X = Pos.AnchorEnd ();
/// anchorButton.Y = Pos.AnchorEnd ();
/// </code>
/// </example>
public static Pos AnchorEnd () { return new PosAnchorEnd (); }
/// <summary>
/// Creates a <see cref="Pos"/> object that is anchored to the end (right side or bottom) of the SuperView,
/// useful to flush the layout from the right or bottom. See also <see cref="Pos.AnchorEnd()"/>, which uses the view
/// dimension to ensure the view is fully visible.
/// </summary>
/// <returns>The <see cref="Pos"/> object anchored to the end (the bottom or the right side).</returns>
/// <param name="offset">The view will be shifted left or up by the amount specified.</param>
/// <example>
/// This sample shows how align a <see cref="Button"/> to the bottom-right of a <see cref="View"/>.
/// This sample shows how align a 10 column wide <see cref="Button"/> to the bottom-right the SuperView.
/// <code>
/// // See Issue #502
/// anchorButton.X = Pos.AnchorEnd () - (Pos.Right (anchorButton) - Pos.Left (anchorButton));
/// anchorButton.Y = Pos.AnchorEnd (1);
/// anchorButton.X = Pos.AnchorEnd (10);
/// anchorButton.Y = 1
/// </code>
/// </example>
public static Pos AnchorEnd (int offset = 0)
public static Pos AnchorEnd (int offset)
{
if (offset < 0)
{
@@ -153,25 +171,17 @@ public class Pos
/// <param name="n">The value to convert to the <see cref="Pos"/>.</param>
public static Pos At (int n) { return new PosAbsolute (n); }
/// <summary>
/// Creates a <see cref="Pos"/> object that tracks the Bottom (Y+Height) coordinate of the specified
/// <see cref="View"/>
/// </summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos Bottom (View view) { return new PosView (view, 3); }
/// <summary>Creates a <see cref="Pos"/> object that can be used to center the <see cref="View"/>.</summary>
/// <returns>The center Pos.</returns>
/// <example>
/// This creates a <see cref="TextField"/>that is centered horizontally, is 50% of the way down, is 30% the height, and
/// This creates a <see cref="TextView"/> centered horizontally, is 50% of the way down, is 30% the height, and
/// is 80% the width of the <see cref="View"/> it added to.
/// <code>
/// 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),
/// };
/// </code>
/// </example>
@@ -197,11 +207,6 @@ public class Pos
/// <returns>A hash code for the current object.</returns>
public override int GetHashCode () { return Anchor (0).GetHashCode (); }
/// <summary>Creates a <see cref="Pos"/> object that tracks the Left (X) position of the specified <see cref="View"/>.</summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos Left (View view) { return new PosView (view, 0); }
/// <summary>Adds a <see cref="Terminal.Gui.Pos"/> to a <see cref="Terminal.Gui.Pos"/>, yielding a new <see cref="Pos"/>.</summary>
/// <param name="left">The first <see cref="Terminal.Gui.Pos"/> to add.</param>
/// <param name="right">The second <see cref="Terminal.Gui.Pos"/> to add.</param>
@@ -214,7 +219,11 @@ public class Pos
}
var newPos = new PosCombine (true, left, right);
SetPosCombine (left, newPos);
if (left is PosView view)
{
view.Target.SetNeedsLayout ();
}
return newPos;
}
@@ -239,75 +248,109 @@ public class Pos
}
var newPos = new PosCombine (false, left, right);
SetPosCombine (left, newPos);
if (left is PosView view)
{
view.Target.SetNeedsLayout ();
}
return newPos;
}
/// <summary>Creates a percentage <see cref="Pos"/> object</summary>
/// <returns>The percent <see cref="Pos"/> object.</returns>
/// <param name="n">A value between 0 and 100 representing the percentage.</param>
/// <param name="percent">A value between 0 and 100 representing the percentage.</param>
/// <example>
/// This creates a <see cref="TextField"/>that is centered horizontally, is 50% of the way down, is 30% the height, and
/// This creates a <see cref="TextField"/> centered horizontally, is 50% of the way down, is 30% the height, and
/// is 80% the width of the <see cref="View"/> it added to.
/// <code>
/// 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),
/// };
/// </code>
/// </example>
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);
}
/// <summary>Creates a <see cref="Pos"/> object that tracks the Top (Y) position of the specified <see cref="View"/>.</summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos Top (View view) { return new PosView (view, Side.Top); }
/// <summary>Creates a <see cref="Pos"/> object that tracks the Top (Y) position of the specified <see cref="View"/>.</summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos Y (View view) { return new PosView (view, Side.Top); }
/// <summary>Creates a <see cref="Pos"/> object that tracks the Left (X) position of the specified <see cref="View"/>.</summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos Left (View view) { return new PosView (view, Side.Left); }
/// <summary>Creates a <see cref="Pos"/> object that tracks the Left (X) position of the specified <see cref="View"/>.</summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos X (View view) { return new PosView (view, Side.Left); }
/// <summary>
/// Creates a <see cref="Pos"/> object that tracks the Bottom (Y+Height) coordinate of the specified
/// <see cref="View"/>
/// </summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos Bottom (View view) { return new PosView (view, Side.Bottom); }
/// <summary>
/// Creates a <see cref="Pos"/> object that tracks the Right (X+Width) coordinate of the specified
/// <see cref="View"/>.
/// </summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos Right (View view) { return new PosView (view, 2); }
/// <summary>Creates a <see cref="Pos"/> object that tracks the Top (Y) position of the specified <see cref="View"/>.</summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos Top (View view) { return new PosView (view, 1); }
/// <summary>Creates a <see cref="Pos"/> object that tracks the Left (X) position of the specified <see cref="View"/>.</summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos X (View view) { return new PosView (view, 0); }
/// <summary>Creates a <see cref="Pos"/> object that tracks the Top (Y) position of the specified <see cref="View"/>.</summary>
/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
/// <param name="view">The <see cref="View"/> that will be tracked.</param>
public static Pos Y (View view) { return new PosView (view, 1); }
public static Pos Right (View view) { return new PosView (view, Side.Right); }
/// <summary>
/// Gets a position that is anchored to a certain point in the layout. This method is typically used
/// internally by the layout system to determine where a View should be positioned.
/// </summary>
/// <param name="width">The width of the area where the View is being positioned (Superview.ContentSize).</param>
/// <returns>
/// An integer representing the calculated position. The way this position is calculated depends on the specific
/// subclass of Pos that is used. For example, PosAbsolute returns a fixed position, PosAnchorEnd returns a
/// position that is anchored to the end of the layout, and so on.
/// </returns>
internal virtual int Anchor (int width) { return 0; }
private static void SetPosCombine (Pos left, PosCombine newPos)
{
var view = left as PosView;
/// <summary>
/// Calculates and returns the position of a <see cref="View"/> object. It takes into account the dimension of the
/// superview and the dimension of the view itself.
/// </summary>
/// <param name="superviewDimension">
/// The dimension of the superview. This could be the width for x-coordinate calculation or the
/// height for y-coordinate calculation.
/// </param>
/// <param name="dim">The dimension of the View. It could be the current width or height.</param>
/// <param name="autosize">Obsolete; to be deprecated.</param>
/// <param name="autoSize">Obsolete; to be deprecated.</param>
/// <returns>
/// The calculated position of the View. The way this position is calculated depends on the specific subclass of Pos
/// that
/// is used.
/// </returns>
internal virtual int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize) { return Anchor (superviewDimension); }
if (view is { })
{
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})"; }
@@ -317,30 +360,58 @@ public class Pos
internal class PosAnchorEnd : Pos
{
private readonly int _offset;
public PosAnchorEnd () { UseDimForOffset = true; }
public PosAnchorEnd (int offset) { _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})"; }
internal override int Anchor (int width) { return width - _offset; }
/// <summary>
/// If true, the offset is the width of the view, if false, the offset is the offset value.
/// </summary>
internal bool UseDimForOffset { get; set; }
public override string ToString () { return UseDimForOffset ? "AnchorEnd()" : $"AnchorEnd({_offset})"; }
internal override int Anchor (int width)
{
if (UseDimForOffset)
{
return width;
}
return width - _offset;
}
internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize)
{
int newLocation = Anchor (superviewDimension);
if (UseDimForOffset)
{
newLocation -= dim.Anchor (superviewDimension);
}
return newLocation;
}
}
internal class PosCenter : Pos
{
public override string ToString () { return "Center"; }
internal override int Anchor (int width) { return width / 2; }
internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize)
{
int newDimension = Math.Max (dim.Calculate (0, superviewDimension, autosize, autoSize), 0);
return Anchor (superviewDimension - newDimension);
}
}
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})"; }
@@ -356,12 +427,25 @@ public class Pos
return la - ra;
}
internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize)
{
int newDimension = dim.Calculate (0, superviewDimension, autosize, autoSize);
int left = _left.Calculate (superviewDimension, dim, autosize, autoSize);
int right = _right.Calculate (superviewDimension, dim, autosize, autoSize);
if (_add)
{
return left + right;
}
return left - right;
}
}
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,78 +453,77 @@ 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<int> n) : Pos
{
private readonly Func<int> _function;
public PosFunc (Func<int> n) { _function = n; }
private readonly Func<int> _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
/// <summary>
/// Describes which side of the view to use for the position.
/// </summary>
public enum Side
{
public readonly View Target;
/// <summary>
/// The left (X) side of the view.
/// </summary>
Left = 0,
private readonly int side;
/// <summary>
/// The top (Y) side of the view.
/// </summary>
Top = 1,
public PosView (View view, int side)
{
Target = view;
this.side = side;
}
/// <summary>
/// The right (X + Width) side of the view.
/// </summary>
Right = 2,
/// <summary>
/// The bottom (Y + Height) side of the view.
/// </summary>
Bottom = 3
}
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)
string sideString = side switch
{
case 0:
tside = "x";
Side.Left => "left",
Side.Top => "top",
Side.Right => "right",
Side.Bottom => "bottom",
_ => "unknown"
};
break;
case 1:
tside = "y";
break;
case 2:
tside = "right";
break;
case 3:
tside = "bottom";
break;
default:
tside = "unknown";
break;
}
if (Target is null)
if (Target == null)
{
throw new NullReferenceException (nameof (Target));
}
return $"View(side={tside},target={Target})";
return $"View(side={sideString},target={Target})";
}
internal override int Anchor (int width)
{
switch (side)
return side switch
{
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;
}
Side.Left => Target.Frame.X,
Side.Top => Target.Frame.Y,
Side.Right => Target.Frame.Right,
Side.Bottom => Target.Frame.Bottom,
_ => 0
};
}
}
}
@@ -486,8 +569,8 @@ public class Pos
/// <see cref="Dim.Fill(int)"/>
/// </term>
/// <description>
/// Creates a <see cref="Dim"/> object that fills the dimension, leaving the specified number
/// of columns for a margin.
/// Creates a <see cref="Dim"/> 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.
/// </description>
/// </item>
/// <item>
@@ -545,11 +628,11 @@ public class Dim
/// <summary>Creates a <see cref="Dim"/> object that tracks the Height of the specified <see cref="View"/>.</summary>
/// <returns>The height <see cref="Dim"/> of the other <see cref="View"/>.</returns>
/// <param name="view">The view that will be tracked.</param>
public static Dim Height (View view) { return new DimView (view, 0); }
public static Dim Height (View view) { return new DimView (view, Dimension.Height); }
/// <summary>Adds a <see cref="Terminal.Gui.Dim"/> to a <see cref="Terminal.Gui.Dim"/>, yielding a new <see cref="Dim"/>.</summary>
/// <param name="left">The first <see cref="Terminal.Gui.Dim"/> to add.</param>
/// <param name="right">The second <see cref="Terminal.Gui.Dim"/> to add.</param>
/// <summary>Adds a <see cref="Dim"/> to a <see cref="Dim"/>, yielding a new <see cref="Dim"/>.</summary>
/// <param name="left">The first <see cref="Dim"/> to add.</param>
/// <param name="right">The second <see cref="Dim"/> to add.</param>
/// <returns>The <see cref="Dim"/> that is the sum of the values of <c>left</c> and <c>right</c>.</returns>
public static Dim operator + (Dim left, Dim right)
{
@@ -559,7 +642,7 @@ public class Dim
}
var newDim = new DimCombine (true, left, right);
SetDimCombine (left, newDim);
(left as DimView)?.Target.SetNeedsLayout ();
return newDim;
}
@@ -570,11 +653,11 @@ public class Dim
public static implicit operator Dim (int n) { return new DimAbsolute (n); }
/// <summary>
/// Subtracts a <see cref="Terminal.Gui.Dim"/> from a <see cref="Terminal.Gui.Dim"/>, yielding a new
/// Subtracts a <see cref="Dim"/> from a <see cref="Dim"/>, yielding a new
/// <see cref="Dim"/>.
/// </summary>
/// <param name="left">The <see cref="Terminal.Gui.Dim"/> to subtract from (the minuend).</param>
/// <param name="right">The <see cref="Terminal.Gui.Dim"/> to subtract (the subtrahend).</param>
/// <param name="left">The <see cref="Dim"/> to subtract from (the minuend).</param>
/// <param name="right">The <see cref="Dim"/> to subtract (the subtrahend).</param>
/// <returns>The <see cref="Dim"/> that is the <c>left</c> minus <c>right</c>.</returns>
public static Dim operator - (Dim left, Dim right)
{
@@ -584,38 +667,40 @@ public class Dim
}
var newDim = new DimCombine (false, left, right);
SetDimCombine (left, newDim);
(left as DimView)?.Target.SetNeedsLayout ();
return newDim;
}
/// <summary>Creates a percentage <see cref="Dim"/> object that is a percentage of the width or height of the SuperView.</summary>
/// <returns>The percent <see cref="Dim"/> object.</returns>
/// <param name="n">A value between 0 and 100 representing the percentage.</param>
/// <param name="r">
/// If <c>true</c> the Percent is computed based on the remaining space after the X/Y anchor positions. If
/// <c>false</c> is computed based on the whole original space.
/// <param name="percent">A value between 0 and 100 representing the percentage.</param>
/// <param name="usePosition">
/// If <see langword="true"/> the dimension is computed using the View's position (<see cref="View.X"/> or
/// <see cref="View.Y"/>).
/// If <see langword="false"/> the dimension is computed using the View's <see cref="View.ContentSize"/>.
/// </param>
/// <example>
/// This initializes a <see cref="TextField"/>that is centered horizontally, is 50% of the way down, is 30% the height,
/// and is 80% the width of the <see cref="View"/> it added to.
/// This initializes a <see cref="TextField"/> that will be centered horizontally, is 50% of the way down, is 30% the
/// height,
/// and is 80% the width of the SuperView.
/// <code>
/// 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),
/// };
/// </code>
/// </example>
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);
}
/// <summary>Creates an Absolute <see cref="Dim"/> from the specified integer value.</summary>
@@ -626,34 +711,63 @@ public class Dim
/// <summary>Creates a <see cref="Dim"/> object that tracks the Width of the specified <see cref="View"/>.</summary>
/// <returns>The width <see cref="Dim"/> of the other <see cref="View"/>.</returns>
/// <param name="view">The view that will be tracked.</param>
public static Dim Width (View view) { return new DimView (view, 1); }
public static Dim Width (View view) { return new DimView (view, Dimension.Width); }
/// <summary>
/// Gets a dimension that is anchored to a certain point in the layout.
/// This method is typically used internally by the layout system to determine the size of a View.
/// </summary>
/// <param name="width">The width of the area where the View is being sized (Superview.ContentSize).</param>
/// <returns>
/// An integer representing the calculated dimension. The way this dimension is calculated depends on the specific
/// subclass of Dim that is used. For example, DimAbsolute returns a fixed dimension, DimFactor returns a
/// dimension that is a certain percentage of the super view's size, and so on.
/// </returns>
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
/// <summary>
/// Calculates and returns the dimension of a <see cref="View"/> object. It takes into account the location of the
/// <see cref="View"/>, its current size, and whether it should automatically adjust its size based on its content.
/// </summary>
/// <param name="location">
/// The starting point from where the size calculation begins. It could be the left edge for width calculation or the
/// top edge for height calculation.
/// </param>
/// <param name="dimension">The current size of the View. It could be the current width or height.</param>
/// <param name="autosize">Obsolete; To be deprecated.</param>
/// <param name="autoSize">Obsolete; To be deprecated.</param>
/// <returns>
/// The calculated size of the View. The way this size is calculated depends on the specific subclass of Dim that
/// is used.
/// </returns>
internal virtual int Calculate (int location, int dimension, int autosize, bool autoSize)
{
private readonly int _n;
public DimAbsolute (int n) { _n = n; }
int newDimension = Math.Max (Anchor (dimension - location), 0);
return autoSize && autosize > newDimension ? autosize : newDimension;
}
internal class DimAbsolute (int n) : Dim
{
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 override int Calculate (int location, int dimension, int autosize, bool autoSize)
{
// DimAbsolute.Anchor (int width) ignores width and returns n
int newDimension = Math.Max (Anchor (0), 0);
return autoSize && autosize > newDimension ? autosize : newDimension;
}
}
internal class DimCombine : Dim
internal class DimCombine (bool add, Dim left, Dim right) : Dim
{
internal bool _add;
internal Dim _left, _right;
public DimCombine (bool add, Dim left, Dim right)
{
_left = left;
_right = right;
_add = add;
}
internal bool _add = add;
internal Dim _left = left, _right = right;
public override string ToString () { return $"Combine({_left}{(_add ? '+' : '-')}{_right})"; }
@@ -669,30 +783,49 @@ public class Dim
return la - ra;
}
internal override int Calculate (int location, int dimension, int autosize, bool autoSize)
{
int leftNewDim = _left.Calculate (location, dimension, autosize, autoSize);
int rightNewDim = _right.Calculate (location, dimension, autosize, autoSize);
int newDimension;
if (_add)
{
newDimension = leftNewDim + rightNewDim;
}
else
{
newDimension = Math.Max (0, leftNewDim - rightNewDim);
}
return autoSize && autosize > newDimension ? autosize : newDimension;
}
}
internal class DimFactor : Dim
internal class DimFactor (float factor, bool remaining = 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 = factor;
private readonly bool _remaining = remaining;
public override bool Equals (object other) { return other is DimFactor f && f._factor == _factor && f._remaining == _remaining; }
public override int GetHashCode () { return _factor.GetHashCode (); }
public bool IsFromRemaining () { return _remaining; }
public override string ToString () { return $"Factor({_factor},{_remaining})"; }
internal override int Anchor (int width) { return (int)(width * _factor); }
internal override int Calculate (int location, int dimension, int autosize, bool autoSize)
{
int newDimension = _remaining ? Math.Max (Anchor (dimension - location), 0) : Anchor (dimension);
return autoSize && autosize > newDimension ? autosize : newDimension;
}
}
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 +833,36 @@ 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<int> n) : Dim
{
private readonly Func<int> _function;
public DimFunc (Func<int> n) { _function = n; }
private readonly Func<int> _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 (); }
}
/// <summary>
///
/// </summary>
public enum Dimension
{
/// <summary>
/// The height dimension.
/// </summary>
Height = 0,
/// <summary>
/// The width dimension.
/// </summary>
Width = 1
}
internal class DimView : Dim
{
private readonly int _side;
private readonly Dimension _side;
public DimView (View view, int side)
internal DimView (View view, Dimension side)
{
Target = view;
_side = side;
@@ -726,29 +874,29 @@ public class Dim
public override string ToString ()
{
if (Target is null)
if (Target == null)
{
throw new NullReferenceException ();
}
string tside = _side switch
{
0 => "Height",
1 => "Width",
_ => "unknown"
};
string sideString = _side switch
{
Dimension.Height => "Height",
Dimension.Width => "Width",
_ => "unknown"
};
return $"View({tside},{Target})";
return $"View({sideString},{Target})";
}
internal override int Anchor (int width)
{
return _side switch
{
0 => Target.Frame.Height,
1 => Target.Frame.Width,
_ => 0
};
{
Dimension.Height => Target.Frame.Height,
Dimension.Width => Target.Frame.Width,
_ => 0
};
}
}
}

View File

@@ -1,5 +1,4 @@
using System.Diagnostics;
using System.IO.Compression;
namespace Terminal.Gui;
@@ -88,11 +87,13 @@ public partial class View
private void SetFrame (Rectangle frame)
{
Rectangle oldViewport = Rectangle.Empty;
var oldViewport = Rectangle.Empty;
if (IsInitialized)
{
oldViewport = Viewport;
}
// This is the only place where _frame should be set directly. Use Frame = or SetFrame instead.
_frame = frame;
@@ -113,7 +114,7 @@ public partial class View
// Adornments don't have SuperViews; use Adornment.FrameToScreen override
// which will give us the screen coordinates of the parent
var parentScreen = adornment.FrameToScreen ();
Rectangle parentScreen = adornment.FrameToScreen ();
// Now add our Frame location
parentScreen.Offset (screen.X, screen.Y);
@@ -142,7 +143,7 @@ public partial class View
{
if (SuperView is null)
{
return new Point (x - Frame.X, y - Frame.Y);
return new (x - Frame.X, y - Frame.Y);
}
Point superViewViewportOffset = SuperView.GetViewportOffsetFromFrame ();
@@ -242,7 +243,8 @@ public partial class View
/// <value>The <see cref="Dim"/> object representing the height of the view (the number of rows).</value>
/// <remarks>
/// <para>
/// The dimension is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="ContentSize"/>.
/// The dimension is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="ContentSize"/>
/// .
/// </para>
/// <para>
/// If set to a relative value (e.g. <see cref="Dim.Fill(int)"/>) the value is indeterminate until the view has
@@ -283,8 +285,8 @@ public partial class View
if (IsAdded && AutoSize && !isValidNewAutoSize)
{
Debug.WriteLine (
@$"Must set AutoSize to false before setting the {nameof (Height)}."
);
@$"Must set AutoSize to false before setting the {nameof (Height)}."
);
AutoSize = false;
}
@@ -299,7 +301,8 @@ public partial class View
/// <value>The <see cref="Dim"/> object representing the width of the view (the number of columns).</value>
/// <remarks>
/// <para>
/// The dimension is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="ContentSize"/>.
/// The dimension is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="ContentSize"/>
/// .
/// </para>
/// <para>
/// If set to a relative value (e.g. <see cref="Dim.Fill(int)"/>) the value is indeterminate until the view has
@@ -330,7 +333,7 @@ public partial class View
if (AutoSize)
{
Debug.WriteLine($@"Must set AutoSize to false before setting {nameof(Width)}.");
Debug.WriteLine ($@"Must set AutoSize to false before setting {nameof (Width)}.");
AutoSize = false;
}
@@ -338,7 +341,7 @@ public partial class View
if (IsAdded && AutoSize && !isValidNewAutoSize)
{
Debug.WriteLine($@"Must set AutoSize to false before setting {nameof(Width)}.");
Debug.WriteLine ($@"Must set AutoSize to false before setting {nameof (Width)}.");
AutoSize = false;
}
@@ -656,6 +659,7 @@ public partial class View
int startOffsetY = y - (start.Frame.Y + viewportOffset.Y);
View? subview = null;
for (int i = start.InternalSubviews.Count - 1; i >= 0; i--)
{
if (start.InternalSubviews [i].Visible
@@ -750,8 +754,8 @@ public partial class View
}
//System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
bool menuVisible = false;
bool statusVisible = false;
var menuVisible = false;
var statusVisible = false;
if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
{
@@ -907,6 +911,7 @@ public partial class View
OnLayoutComplete (new (ContentSize));
}
private void LayoutSubview (View v, Size contentSize)
{
v.SetRelativeLayout (contentSize);
@@ -964,6 +969,7 @@ public partial class View
SetNeedsLayout ();
}
}
internal bool LayoutNeeded { get; private set; } = true;
/// <summary>
@@ -995,11 +1001,11 @@ public partial class View
/// <see cref="Height"/>).
/// </summary>
/// <remarks>
/// <para>
/// If <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, or <see cref="Height"/> are
/// absolute, they will be updated to reflect the new size and position of the view. Otherwise, they
/// are left unchanged.
/// </para>
/// <para>
/// If <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, or <see cref="Height"/> are
/// absolute, they will be updated to reflect the new size and position of the view. Otherwise, they
/// are left unchanged.
/// </para>
/// </remarks>
/// <param name="superviewContentSize">
/// The size of the SuperView's content (nominally the same as <c>this.SuperView.ContentSize</c>).
@@ -1011,175 +1017,25 @@ public partial class View
Debug.Assert (_width is { });
Debug.Assert (_height is { });
int newX, newW, newY, newH;
var autosize = Size.Empty;
var autoSize = Size.Empty;
if (AutoSize)
{
// 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 = GetAutoSize ();
}
// TODO: Since GetNewLocationAndDimension does not depend on View, it can be moved into PosDim.cs
// TODO: to make architecture more clean. Do this after DimAuto is implemented and the
// TODO: View.AutoSize stuff is removed.
int newX = _x.Calculate (superviewContentSize.Width, _width, autoSize.Width, AutoSize);
int newW = _width.Calculate (newX, superviewContentSize.Width, autoSize.Width, AutoSize);
int newY = _y.Calculate (superviewContentSize.Height, _height, autoSize.Height, AutoSize);
int newH = _height.Calculate (newY, superviewContentSize.Height, autoSize.Height, AutoSize);
// Returns the new dimension (width or height) and location (x or y) for the View given
// the superview's Viewport
// the current Pos (View.X or View.Y)
// the current Dim (View.Width or View.Height)
// This method is called recursively if pos is Pos.PosCombine
(int newLocation, int newDimension) GetNewLocationAndDimension (
bool width,
Size superviewContentSize,
Pos pos,
Dim dim,
int autosizeDimension
)
{
// Gets the new dimension (width or height, dependent on `width`) of the given Dim given:
// location: the current location (x or y)
// dimension: the new dimension (width or height) (if relevant for Dim type)
// autosize: the size to use if autosize = true
// This method is recursive if d is Dim.DimCombine
int GetNewDimension (Dim d, int location, int dimension, int autosize)
{
int newDimension;
Rectangle newFrame = new (newX, newY, newW, newH);
switch (d)
{
case Dim.DimCombine combine:
// TODO: Move combine logic into DimCombine?
int leftNewDim = GetNewDimension (combine._left, location, dimension, autosize);
int rightNewDim = GetNewDimension (combine._right, location, dimension, autosize);
if (combine._add)
{
newDimension = leftNewDim + rightNewDim;
}
else
{
newDimension = leftNewDim - rightNewDim;
}
newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
break;
case Dim.DimFactor factor when !factor.IsFromRemaining ():
newDimension = d.Anchor (dimension);
newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
break;
case Dim.DimAbsolute:
// DimAbsolute.Anchor (int width) ignores width and returns n
newDimension = Math.Max (d.Anchor (0), 0);
// BUGBUG: AutoSize does two things: makes text fit AND changes the view's dimensions
newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
break;
case Dim.DimFill:
default:
newDimension = Math.Max (d.Anchor (dimension - location), 0);
newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
break;
}
return newDimension;
}
int newDimension, newLocation;
int superviewDimension = width ? superviewContentSize.Width : superviewContentSize.Height;
// Determine new location
switch (pos)
{
case Pos.PosCenter posCenter:
// For Center, the dimension is dependent on location, but we need to force getting the dimension first
// using a location of 0
newDimension = Math.Max (GetNewDimension (dim, 0, superviewDimension, autosizeDimension), 0);
newLocation = posCenter.Anchor (superviewDimension - newDimension);
newDimension = Math.Max (
GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
0
);
break;
case Pos.PosCombine combine:
// TODO: Move combine logic into PosCombine?
int left, right;
(left, newDimension) = GetNewLocationAndDimension (
width,
superviewContentSize,
combine._left,
dim,
autosizeDimension
);
(right, newDimension) = GetNewLocationAndDimension (
width,
superviewContentSize,
combine._right,
dim,
autosizeDimension
);
if (combine._add)
{
newLocation = left + right;
}
else
{
newLocation = left - right;
}
newDimension = Math.Max (
GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
0
);
break;
case Pos.PosAnchorEnd:
case Pos.PosAbsolute:
case Pos.PosFactor:
case Pos.PosFunc:
case Pos.PosView:
default:
newLocation = pos?.Anchor (superviewDimension) ?? 0;
newDimension = Math.Max (
GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
0
);
break;
}
return (newLocation, newDimension);
}
// horizontal/width
(newX, newW) = GetNewLocationAndDimension (true, superviewContentSize, _x, _width, autosize.Width);
// vertical/height
(newY, newH) = GetNewLocationAndDimension (false, superviewContentSize, _y, _height, autosize.Height);
Rectangle r = new (newX, newY, newW, newH);
if (Frame != r)
if (Frame != newFrame)
{
// Set the frame. Do NOT use `Frame` as it overwrites X, Y, Width, and Height, making
// the view LayoutStyle.Absolute.
SetFrame (r);
SetFrame (newFrame);
if (_x is Pos.PosAbsolute)
{
@@ -1207,18 +1063,18 @@ public partial class View
if (AutoSize)
{
if (autosize.Width == 0 || autosize.Height == 0)
if (autoSize.Width == 0 || autoSize.Height == 0)
{
// Set the frame. Do NOT use `Frame` as it overwrites X, Y, Width, and Height, making
// the view LayoutStyle.Absolute.
SetFrame (_frame with { Size = autosize });
SetFrame (_frame with { Size = autoSize });
if (autosize.Width == 0)
if (autoSize.Width == 0)
{
_width = 0;
}
if (autosize.Height == 0)
if (autoSize.Height == 0)
{
_height = 0;
}
@@ -1432,4 +1288,4 @@ public partial class View
public bool ValidatePosDim { get; set; }
#endregion
}
}

View File

@@ -38,7 +38,7 @@ public class Adornments : Scenario
app.Add (window);
var tf1 = new TextField { Width = 10, Text = "TextField" };
var color = new ColorPicker { Title = "BG", BoxHeight = 1, BoxWidth = 1, X = Pos.AnchorEnd (11) };
var color = new ColorPicker { Title = "BG", BoxHeight = 1, BoxWidth = 1, X = Pos.AnchorEnd () };
color.BorderStyle = LineStyle.RoundedDotted;
color.ColorChanged += (s, e) =>
@@ -68,15 +68,16 @@ public class Adornments : Scenario
};
label.Border.Thickness = new (1, 3, 1, 1);
var btnButtonInWindow = new Button { X = Pos.AnchorEnd (10), Y = Pos.AnchorEnd (1), Text = "Button" };
var btnButtonInWindow = new Button { X = Pos.AnchorEnd (), Y = Pos.AnchorEnd (), Text = "Button" };
var tv = new Label
var labelAnchorEnd = new Label
{
AutoSize = false,
Y = Pos.AnchorEnd (3),
Width = 25,
Height = Dim.Fill (),
Text = "Label\nY=AnchorEnd(3),Height=Dim.Fill()"
Y = Pos.AnchorEnd (),
Width = 40,
Height = Dim.Percent(20),
Text = "Label\nY=AnchorEnd(),Height=Dim.Percent(10)",
ColorScheme = Colors.ColorSchemes ["Error"]
};
window.Margin.Data = "Margin";
@@ -94,7 +95,7 @@ public class Adornments : Scenario
};
longLabel.TextFormatter.WordWrap = true;
window.Add (tf1, color, button, label, btnButtonInWindow, tv, longLabel);
window.Add (tf1, color, button, label, btnButtonInWindow, labelAnchorEnd, longLabel);
editor.Initialized += (s, e) => { editor.ViewToEdit = window; };

View File

@@ -147,7 +147,7 @@ public class AllViewsTester : Scenario
};
_settingsPane.Add (_computedCheckBox);
string [] radioItems = { "_Percent(x)", "_AnchorEnd(x)", "_Center", "A_t(x)" };
string [] radioItems = { "_Percent(x)", "_AnchorEnd", "_Center", "A_t(x)" };
_locationFrame = new FrameView
{
@@ -179,7 +179,7 @@ public class AllViewsTester : Scenario
_locationFrame.Add (_xRadioGroup);
radioItems = new [] { "P_ercent(y)", "A_nchorEnd(y)", "C_enter", "At(_y)" };
radioItems = new [] { "P_ercent(y)", "A_nchorEnd", "C_enter", "At(_y)" };
label = new Label { X = Pos.Right (_xRadioGroup) + 1, Y = 0, Text = "Y:" };
_locationFrame.Add (label);
_yText = new TextField { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_yVal}" };
@@ -388,7 +388,7 @@ public class AllViewsTester : Scenario
view.X = _xRadioGroup.SelectedItem switch
{
0 => Pos.Percent (_xVal),
1 => Pos.AnchorEnd (_xVal),
1 => Pos.AnchorEnd (),
2 => Pos.Center (),
3 => Pos.At (_xVal),
_ => view.X
@@ -397,7 +397,7 @@ public class AllViewsTester : Scenario
view.Y = _yRadioGroup.SelectedItem switch
{
0 => Pos.Percent (_yVal),
1 => Pos.AnchorEnd (_yVal),
1 => Pos.AnchorEnd (),
2 => Pos.Center (),
3 => Pos.At (_yVal),
_ => view.Y

View File

@@ -34,12 +34,12 @@ public class Animation : Scenario
win.Add (imageView);
var lbl = new Label { Y = Pos.AnchorEnd (2), Text = "Image by Wikiscient" };
var lbl = new Label { Y = Pos.AnchorEnd (), Text = "Image by Wikiscient" };
win.Add (lbl);
var lbl2 = new Label
{
Y = Pos.AnchorEnd (1), Text = "https://commons.wikimedia.org/wiki/File:Spinning_globe.gif"
X = Pos.AnchorEnd(), Y = Pos.AnchorEnd (), Text = "https://commons.wikimedia.org/wiki/File:Spinning_globe.gif"
};
win.Add (lbl2);

View File

@@ -8,8 +8,15 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("Text and Formatting")]
public class BasicColors : Scenario
{
public override void Setup ()
public override void Main ()
{
Application.Init ();
Window app = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
};
var vx = 30;
var x = 30;
var y = 14;
@@ -31,7 +38,7 @@ public class BasicColors : Scenario
Text = bg.ToString (),
TextDirection = TextDirection.TopBottom_LeftRight
};
Win.Add (vl);
app.Add (vl);
var hl = new Label
{
@@ -44,7 +51,7 @@ public class BasicColors : Scenario
ColorScheme = new ColorScheme { Normal = attr },
Text = bg.ToString ()
};
Win.Add (hl);
app.Add (hl);
vx++;
foreach (ColorName fg in colors)
@@ -56,7 +63,7 @@ public class BasicColors : Scenario
{
ColorScheme = new ColorScheme { Normal = c }, X = x, Y = y, Text = t [^1].ToString ()
};
Win.Add (l);
app.Add (l);
x++;
}
@@ -64,24 +71,24 @@ public class BasicColors : Scenario
y++;
}
Win.Add (
app.Add (
new Label { X = Pos.AnchorEnd (36), Text = "Mouse over to get the Attribute:" }
);
Win.Add (new Label { X = Pos.AnchorEnd (35), Y = 2, Text = "Foreground:" });
app.Add (new Label { X = Pos.AnchorEnd (35), Y = 2, Text = "Foreground:" });
var lblForeground = new Label { X = Pos.AnchorEnd (23), Y = 2 };
Win.Add (lblForeground);
app.Add (lblForeground);
var viewForeground = new View { X = Pos.AnchorEnd (2), Y = 2, ColorScheme = new ColorScheme (), Text = " " };
Win.Add (viewForeground);
app.Add (viewForeground);
Win.Add (new Label { X = Pos.AnchorEnd (35), Y = 4, Text = "Background:" });
app.Add (new Label { X = Pos.AnchorEnd (35), Y = 4, Text = "Background:" });
var lblBackground = new Label { X = Pos.AnchorEnd (23), Y = 4 };
Win.Add (lblBackground);
app.Add (lblBackground);
var viewBackground = new View { X = Pos.AnchorEnd (2), Y = 4, ColorScheme = new ColorScheme (), Text = " " };
Win.Add (viewBackground);
app.Add (viewBackground);
Application.MouseEvent += (s, e) =>
{
@@ -103,5 +110,8 @@ public class BasicColors : Scenario
new ColorScheme (viewBackground.ColorScheme) { Normal = new Attribute (back, back) };
}
};
Application.Run (app);
app.Dispose ();
}
}

View File

@@ -29,7 +29,7 @@ public class Buttons : Scenario
// This is the default button (IsDefault = true); if user presses ENTER in the TextField
// the scenario will quit
var defaultButton = new Button { X = Pos.Center (), Y = Pos.AnchorEnd (1), IsDefault = true, Text = "_Quit" };
var defaultButton = new Button { X = Pos.Center (), Y = Pos.AnchorEnd (), IsDefault = true, Text = "_Quit" };
defaultButton.Accept += (s, e) => Application.RequestStop ();
main.Add (defaultButton);
@@ -459,7 +459,7 @@ public class Buttons : Scenario
_up = new ()
{
AutoSize = false,
X = Pos.AnchorEnd (1),
X = Pos.AnchorEnd (),
Y = Pos.Top (_number),
Height = 1,
Width = 1,

View File

@@ -24,42 +24,45 @@ public class ColorPickers : Scenario
private ColorPicker foregroundColorPicker;
/// <summary>Setup the scenario.</summary>
public override void Setup ()
public override void Main ()
{
// Scenario Window's.
Win.Title = GetName ();
Application.Init ();
Window app = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
};
// Foreground ColorPicker.
foregroundColorPicker = new ColorPicker { Title = "Foreground Color", BorderStyle = LineStyle.Single };
foregroundColorPicker.ColorChanged += ForegroundColor_ColorChanged;
Win.Add (foregroundColorPicker);
app.Add (foregroundColorPicker);
_foregroundColorLabel = new Label
{
X = Pos.Left (foregroundColorPicker), Y = Pos.Bottom (foregroundColorPicker) + 1
};
Win.Add (_foregroundColorLabel);
app.Add (_foregroundColorLabel);
// Background ColorPicker.
backgroundColorPicker = new ColorPicker
{
Title = "Background Color",
// TODO: Replace with Pos.AnchorEnd () when #2900 is done
X = Pos.AnchorEnd ((8 * 4) + 2), // 8 box * 4 width + 2 for border
X = Pos.AnchorEnd (),
BoxHeight = 1,
BoxWidth = 4,
BorderStyle = LineStyle.Single
BorderStyle = LineStyle.Single,
};
//backgroundColorPicker.X = Pos.AnchorEnd () - (Pos.Right (backgroundColorPicker) - Pos.Left (backgroundColorPicker));
backgroundColorPicker.ColorChanged += BackgroundColor_ColorChanged;
Win.Add (backgroundColorPicker);
_backgroundColorLabel = new Label ();
app.Add (backgroundColorPicker);
_backgroundColorLabel = new Label ()
{
X = Pos.AnchorEnd (),
Y = Pos.Bottom (backgroundColorPicker) + 1
};
_backgroundColorLabel.X =
Pos.AnchorEnd () - (Pos.Right (_backgroundColorLabel) - Pos.Left (_backgroundColorLabel));
_backgroundColorLabel.Y = Pos.Bottom (backgroundColorPicker) + 1;
Win.Add (_backgroundColorLabel);
app.Add (_backgroundColorLabel);
// Demo Label.
_demoView = new View
@@ -74,12 +77,15 @@ public class ColorPickers : Scenario
Height = 5,
Width = 20
};
Win.Add (_demoView);
app.Add (_demoView);
// Set default colors.
foregroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Foreground.GetClosestNamedColor ();
backgroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Background.GetClosestNamedColor ();
Win.Initialized += (s, e) => Win.LayoutSubviews ();
app.Initialized += (s, e) => app.LayoutSubviews ();
Application.Run (app);
app.Dispose ();
}
/// <summary>Fired when background color is changed.</summary>

View File

@@ -13,17 +13,15 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("Layout")]
public class ComputedLayout : Scenario
{
public override void Init ()
public override void Main ()
{
Application.Init ();
ConfigurationManager.Themes.Theme = Theme;
ConfigurationManager.Apply ();
Top = new ();
Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
}
public override void Setup ()
{
Window app = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
};
// Demonstrate using Dim to create a horizontal ruler that always measures the parent window's width
const string rule = "|123456789";
@@ -38,7 +36,7 @@ public class ComputedLayout : Scenario
Text = rule
};
Top.Add (horizontalRuler);
app.Add (horizontalRuler);
// Demonstrate using Dim to create a vertical ruler that always measures the parent window's height
const string vrule = "|\n1\n2\n3\n4\n5\n6\n7\n8\n9\n";
@@ -54,7 +52,7 @@ public class ComputedLayout : Scenario
Text = vrule
};
Top.LayoutComplete += (s, a) =>
app.LayoutComplete += (s, a) =>
{
horizontalRuler.Text =
rule.Repeat ((int)Math.Ceiling (horizontalRuler.Viewport.Width / (double)rule.Length)) [
@@ -65,15 +63,15 @@ public class ComputedLayout : Scenario
[..(verticalRuler.Viewport.Height * 2)];
};
Top.Add (verticalRuler);
app.Add (verticalRuler);
// Demonstrate At - Using Pos.At to locate a view in an absolute location
var atButton = new Button { Text = "At(2,1)", X = Pos.At (2), Y = Pos.At (1) };
Top.Add (atButton);
app.Add (atButton);
// Throw in a literal absolute - Should function identically to above
var absoluteButton = new Button { Text = "X = 30, Y = 1", X = 30, Y = 1 };
Top.Add (absoluteButton);
app.Add (absoluteButton);
// Demonstrate using Dim to create a window that fills the parent with a margin
var margin = 10;
@@ -84,7 +82,7 @@ public class ComputedLayout : Scenario
subWin.Title =
$"{subWin.GetType ().Name} {{X={subWin.X},Y={subWin.Y},Width={subWin.Width},Height={subWin.Height}}}";
};
Top.Add (subWin);
app.Add (subWin);
var i = 1;
var txt = "Resize the terminal to see computed layout in action.";
@@ -209,7 +207,7 @@ public class ComputedLayout : Scenario
}
);
frameView.Add (labelList.ToArray ());
Top.Add (frameView);
app.Add (frameView);
frameView = new FrameView
{
@@ -223,7 +221,7 @@ public class ComputedLayout : Scenario
fv.Title =
$"{frameView.GetType ().Name} {{X={fv.X},Y={fv.Y},Width={fv.Width},Height={fv.Height}}}";
};
Top.Add (frameView);
app.Add (frameView);
// Demonstrate Dim & Pos using percentages - a TextField that is 30% height and 80% wide
var textView = new TextView
@@ -237,7 +235,7 @@ public class ComputedLayout : Scenario
textView.Text =
"This TextView should horizontally & vertically centered and \n10% of the screeen height, and 80% of its width.";
Top.Add (textView);
app.Add (textView);
var oddballButton = new Button
{
@@ -245,7 +243,7 @@ public class ComputedLayout : Scenario
X = Pos.Center (),
Y = Pos.Bottom (textView) + 1
};
Top.Add (oddballButton);
app.Add (oddballButton);
#region Issue2358
@@ -253,19 +251,19 @@ public class ComputedLayout : Scenario
// Until https://github.com/gui-cs/Terminal.Gui/issues/2358 is fixed these won't work right
oddballButton = new Button { Text = "Center + 0", X = Pos.Center () + 0, Y = Pos.Bottom (oddballButton) };
Top.Add (oddballButton);
app.Add (oddballButton);
oddballButton = new Button { Text = "Center + 1", X = Pos.Center () + 1, Y = Pos.Bottom (oddballButton) };
Top.Add (oddballButton);
app.Add (oddballButton);
oddballButton = new Button { Text = "0 + Center", X = 0 + Pos.Center (), Y = Pos.Bottom (oddballButton) };
Top.Add (oddballButton);
app.Add (oddballButton);
oddballButton = new Button { Text = "1 + Center", X = 1 + Pos.Center (), Y = Pos.Bottom (oddballButton) };
Top.Add (oddballButton);
app.Add (oddballButton);
oddballButton = new Button { Text = "Center - 1", X = Pos.Center () - 1, Y = Pos.Bottom (oddballButton) };
Top.Add (oddballButton);
app.Add (oddballButton);
// This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
// The `- Pos.Percent(5)` is there so at least something is visible
@@ -275,7 +273,7 @@ public class ComputedLayout : Scenario
X = Pos.Center () + Pos.Center () - Pos.Percent (50),
Y = Pos.Bottom (oddballButton)
};
Top.Add (oddballButton);
app.Add (oddballButton);
// This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
// The `- Pos.Percent(5)` is there so at least something is visible
@@ -285,7 +283,7 @@ public class ComputedLayout : Scenario
X = Pos.Percent (50) + Pos.Center () - Pos.Percent (50),
Y = Pos.Bottom (oddballButton)
};
Top.Add (oddballButton);
app.Add (oddballButton);
// This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50)
// The `- Pos.Percent(5)` is there so at least something is visible
@@ -295,7 +293,7 @@ public class ComputedLayout : Scenario
X = Pos.Center () + Pos.Percent (50) - Pos.Percent (50),
Y = Pos.Bottom (oddballButton)
};
Top.Add (oddballButton);
app.Add (oddballButton);
#endregion
@@ -306,29 +304,29 @@ public class ComputedLayout : Scenario
X = Pos.Center () + Pos.Center () - Pos.Percent (50),
Y = Pos.Bottom (oddballButton)
};
Top.Add (oddballButton);
app.Add (oddballButton);
// This demonstrates combining Percents)
oddballButton = new Button
{
Text = "Percent(40) + Percent(10)", X = Pos.Percent (40) + Pos.Percent (10), Y = Pos.Bottom (oddballButton)
};
Top.Add (oddballButton);
app.Add (oddballButton);
// Demonstrate AnchorEnd - Button is anchored to bottom/right
var anchorButton = new Button { Text = "Button using AnchorEnd", Y = Pos.AnchorEnd () - 1 };
anchorButton.X = Pos.AnchorEnd () - (Pos.Right (anchorButton) - Pos.Left (anchorButton));
var anchorButton = new Button { Text = "Button using AnchorEnd", Y = Pos.AnchorEnd ()};
anchorButton.X = Pos.AnchorEnd ();
anchorButton.Accept += (s, e) =>
{
// This demonstrates how to have a dynamically sized button
// Each time the button is clicked the button's text gets longer
// The call to Top.LayoutSubviews causes the Computed layout to
// The call to app.LayoutSubviews causes the Computed layout to
// get updated.
anchorButton.Text += "!";
Top.LayoutSubviews ();
app.LayoutSubviews ();
};
Top.Add (anchorButton);
app.Add (anchorButton);
// Demonstrate AnchorEnd(n)
// This is intentionally convoluted to illustrate potential bugs.
@@ -342,7 +340,7 @@ public class ComputedLayout : Scenario
X = 5,
Y = Pos.AnchorEnd (2)
};
Top.Add (anchorEndLabel1);
app.Add (anchorEndLabel1);
// Demonstrate DimCombine (via AnchorEnd(n) - 1)
// This is intentionally convoluted to illustrate potential bugs.
@@ -357,22 +355,22 @@ public class ComputedLayout : Scenario
X = 5,
Y = Pos.AnchorEnd (2) - 1 // Pos.Combine
};
Top.Add (anchorEndLabel2);
app.Add (anchorEndLabel2);
// Show positioning vertically using Pos.AnchorEnd via Pos.Combine
var leftButton = new Button
{
Text = "Left", Y = Pos.AnchorEnd () - 1 // Pos.Combine
Text = "Left", Y = Pos.AnchorEnd (0) - 1 // Pos.Combine
};
leftButton.Accept += (s, e) =>
{
// This demonstrates how to have a dynamically sized button
// Each time the button is clicked the button's text gets longer
// The call to Top.LayoutSubviews causes the Computed layout to
// The call to app.LayoutSubviews causes the Computed layout to
// get updated.
leftButton.Text += "!";
Top.LayoutSubviews ();
app.LayoutSubviews ();
};
// show positioning vertically using Pos.AnchorEnd
@@ -385,10 +383,10 @@ public class ComputedLayout : Scenario
{
// This demonstrates how to have a dynamically sized button
// Each time the button is clicked the button's text gets longer
// The call to Top.LayoutSubviews causes the Computed layout to
// The call to app.LayoutSubviews causes the Computed layout to
// get updated.
centerButton.Text += "!";
Top.LayoutSubviews ();
app.LayoutSubviews ();
};
// show positioning vertically using another window and Pos.Bottom
@@ -398,18 +396,21 @@ public class ComputedLayout : Scenario
{
// This demonstrates how to have a dynamically sized button
// Each time the button is clicked the button's text gets longer
// The call to Top.LayoutSubviews causes the Computed layout to
// The call to app.LayoutSubviews causes the Computed layout to
// get updated.
rightButton.Text += "!";
Top.LayoutSubviews ();
app.LayoutSubviews ();
};
// Center three buttons with 5 spaces between them
leftButton.X = Pos.Left (centerButton) - (Pos.Right (leftButton) - Pos.Left (leftButton)) - 5;
rightButton.X = Pos.Right (centerButton) + 5;
Top.Add (leftButton);
Top.Add (centerButton);
Top.Add (rightButton);
app.Add (leftButton);
app.Add (centerButton);
app.Add (rightButton);
Application.Run (app);
app.Dispose ();
}
}

View File

@@ -321,7 +321,7 @@ public class ContentScrolling : Scenario
// Add demo views to show that things work correctly
var textField = new TextField { X = 20, Y = 7, Width = 15, Text = "Test TextField" };
var colorPicker = new ColorPicker { Title = "BG", BoxHeight = 1, BoxWidth = 1, X = Pos.AnchorEnd (11), Y = 10 };
var colorPicker = new ColorPicker { Title = "BG", BoxHeight = 1, BoxWidth = 1, X = Pos.AnchorEnd (), Y = 10 };
colorPicker.BorderStyle = LineStyle.RoundedDotted;
colorPicker.ColorChanged += (s, e) =>
@@ -358,18 +358,9 @@ public class ContentScrolling : Scenario
charMap.Accept += (s, e) =>
MessageBox.Query (20, 7, "Hi", $"Am I a {view.GetType ().Name}?", "Yes", "No");
var buttonAnchoredRight = new Button
var buttonAnchored = new Button
{
X = Pos.AnchorEnd (10), Y = 0, Text = "Button"
};
var labelAnchoredBottomLeft = new Label
{
AutoSize = false,
Y = Pos.AnchorEnd (3),
Width = 25,
Height = Dim.Fill (),
Text = "Label\nY=AnchorEnd(3),Height=Dim.Fill()"
X = Pos.AnchorEnd (), Y = Pos.AnchorEnd (), Text = "Bottom Right"
};
view.Margin.Data = "Margin";
@@ -380,7 +371,7 @@ public class ContentScrolling : Scenario
view.Padding.Data = "Padding";
view.Add (buttonAnchoredRight, textField, colorPicker, charMap, textView, labelAnchoredBottomLeft);
view.Add (buttonAnchored, textField, colorPicker, charMap, textView);
var longLabel = new Label
{

View File

@@ -604,7 +604,7 @@ public class DynamicMenuBar : Scenario
var _btnRemoveMenuBar = new Button { Y = 1, Text = "Remove a MenuBar" };
_btnRemoveMenuBar.X = Pos.AnchorEnd () - (Pos.Right (_btnRemoveMenuBar) - Pos.Left (_btnRemoveMenuBar));
_btnRemoveMenuBar.X = Pos.AnchorEnd (0) - (Pos.Right (_btnRemoveMenuBar) - Pos.Left (_btnRemoveMenuBar));
_frmMenu.Add (_btnRemoveMenuBar);
var _btnPrevious = new Button
@@ -614,7 +614,7 @@ public class DynamicMenuBar : Scenario
_frmMenu.Add (_btnPrevious);
var _btnAdd = new Button { Y = Pos.Top (_btnPrevious) + 2, Text = " Add " };
_btnAdd.X = Pos.AnchorEnd () - (Pos.Right (_btnAdd) - Pos.Left (_btnAdd));
_btnAdd.X = Pos.AnchorEnd ();
_frmMenu.Add (_btnAdd);
var _btnNext = new Button { X = Pos.X (_btnAdd), Y = Pos.Top (_btnPrevious), Text = ">" };

View File

@@ -371,11 +371,11 @@ public class DynamicStatusBar : Scenario
var _btnRemoveStatusBar = new Button { Y = 1, Text = "Remove a StatusBar" };
_btnRemoveStatusBar.X = Pos.AnchorEnd () - (Pos.Right (_btnRemoveStatusBar) - Pos.Left (_btnRemoveStatusBar));
_btnRemoveStatusBar.X = Pos.AnchorEnd ();
_frmStatusBar.Add (_btnRemoveStatusBar);
var _btnAdd = new Button { Y = Pos.Top (_btnRemoveStatusBar) + 2, Text = " Add " };
_btnAdd.X = Pos.AnchorEnd () - (Pos.Right (_btnAdd) - Pos.Left (_btnAdd));
_btnAdd.X = Pos.AnchorEnd (0);
_frmStatusBar.Add (_btnAdd);
_lstItems = new ListView

View File

@@ -4,108 +4,102 @@ namespace UICatalog.Scenarios;
[ScenarioMetadata ("HotKeys", "Demonstrates how HotKeys work.")]
[ScenarioCategory ("Controls")]
[ScenarioCategory("Mouse and Keyboard")]
[ScenarioCategory ("Mouse and Keyboard")]
public class HotKeys : Scenario
{
public override void Init ()
public override void Main ()
{
Application.Init ();
ConfigurationManager.Themes.Theme = Theme;
ConfigurationManager.Apply ();
Top = new ();
Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
Top.BorderStyle = LineStyle.RoundedDotted;
Top.Title = $"{Application.QuitKey} to _Quit - Scenario: {GetName ()}";
}
public override void Run ()
{
Window app = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
};
var textViewLabel = new Label { Text = "_TextView:", X = 0, Y = 0 };
Top.Add (textViewLabel);
var textField = new TextField (){ X = Pos.Right (textViewLabel) + 1, Y = 0, Width = 10 };
Top.Add (textField);
app.Add (textViewLabel);
var textField = new TextField { X = Pos.Right (textViewLabel) + 1, Y = 0, Width = 10 };
app.Add (textField);
var viewLabel = new Label { Text = "_View:", X = 0, Y = Pos.Bottom (textField) + 1 };
Top.Add (viewLabel);
app.Add (viewLabel);
var view = new View () {
Title = "View (_focusable)",
Text = "Text renders _Underscore",
var view = new View
{
Title = "View (_focusable)",
Text = "Text renders _Underscore",
CanFocus = true,
X = Pos.Right (viewLabel) + 1, Y = Pos.Top (viewLabel), Width = 30, Height = 3,
BorderStyle = LineStyle.Dashed,
BorderStyle = LineStyle.Dashed
};
Top.Add (view);
app.Add (view);
viewLabel = new Label { Text = "Vi_ew:", X = 0, Y = Pos.Bottom (view) + 1 };
Top.Add (viewLabel);
viewLabel = new() { Text = "Vi_ew:", X = 0, Y = Pos.Bottom (view) + 1 };
app.Add (viewLabel);
view = new View ()
view = new()
{
Title = "View (n_ot focusable)",
Text = "Text renders _Underscore",
X = Pos.Right (viewLabel) + 1, Y = Pos.Top (viewLabel), Width = 30, Height = 3,
BorderStyle = LineStyle.Dashed,
BorderStyle = LineStyle.Dashed
};
Top.Add (view);
app.Add (view);
var labelWithFrameLabel = new Label { Text = "_Label with Frame:", X = 0, Y = Pos.Bottom (view) + 1 };
Top.Add (labelWithFrameLabel);
app.Add (labelWithFrameLabel);
var labelWithFrameFocusable = new Label ()
var labelWithFrameFocusable = new Label
{
AutoSize = false,
Title = "Label _with Frame (focusable)",
CanFocus = true,
X = Pos.Right (labelWithFrameLabel) + 1, Y = Pos.Top (labelWithFrameLabel), Width = 40, Height = 3,
BorderStyle = LineStyle.Dashed,
BorderStyle = LineStyle.Dashed
};
Top.Add (labelWithFrameFocusable);
app.Add (labelWithFrameFocusable);
labelWithFrameLabel = new Label { Text = "L_abel with Frame:", X = 0, Y = Pos.Bottom (labelWithFrameFocusable) + 1 };
Top.Add (labelWithFrameLabel);
labelWithFrameLabel = new() { Text = "L_abel with Frame:", X = 0, Y = Pos.Bottom (labelWithFrameFocusable) + 1 };
app.Add (labelWithFrameLabel);
var labelWithFrame = new Label ()
var labelWithFrame = new Label
{
AutoSize = false,
Title = "Label with Frame (_not focusable)",
X = Pos.Right (labelWithFrameLabel) + 1, Y = Pos.Top (labelWithFrameLabel), Width = 40, Height = 3,
BorderStyle = LineStyle.Dashed,
BorderStyle = LineStyle.Dashed
};
Top.Add (labelWithFrame);
app.Add (labelWithFrame);
var buttonWithFrameLabel = new Label { Text = "_Button with Frame:", X = 0, Y = Pos.Bottom (labelWithFrame) + 1 };
Top.Add (buttonWithFrameLabel);
app.Add (buttonWithFrameLabel);
var buttonWithFrameFocusable = new Button ()
var buttonWithFrameFocusable = new Button
{
AutoSize = false,
Title = "B_utton with Frame (focusable)",
CanFocus = true,
X = Pos.Right (buttonWithFrameLabel) + 1, Y = Pos.Top (buttonWithFrameLabel), Width = 40, Height = 3,
BorderStyle = LineStyle.Dashed,
BorderStyle = LineStyle.Dashed
};
Top.Add (buttonWithFrameFocusable);
app.Add (buttonWithFrameFocusable);
buttonWithFrameLabel = new Label { Text = "Butt_on with Frame:", X = 0, Y = Pos.Bottom (buttonWithFrameFocusable) + 1 };
Top.Add (buttonWithFrameLabel);
buttonWithFrameLabel = new() { Text = "Butt_on with Frame:", X = 0, Y = Pos.Bottom (buttonWithFrameFocusable) + 1 };
app.Add (buttonWithFrameLabel);
var buttonWithFrame = new Button ()
var buttonWithFrame = new Button
{
AutoSize = false,
Title = "Button with Frame (not focusab_le)",
X = Pos.Right (buttonWithFrameLabel) + 1, Y = Pos.Top (buttonWithFrameLabel), Width = 40, Height = 3,
CanFocus = false,
BorderStyle = LineStyle.Dashed,
BorderStyle = LineStyle.Dashed
};
Top.Add (buttonWithFrame);
app.Add (buttonWithFrame);
var checkboxWithFrameLabel = new Label { Text = "_Checkbox with Frame:", X = 0, Y = Pos.Bottom (buttonWithFrame) + 1 };
Top.Add (checkboxWithFrameLabel);
app.Add (checkboxWithFrameLabel);
var checkboxWithFrameFocusable = new CheckBox
{
@@ -113,12 +107,12 @@ public class HotKeys : Scenario
Title = "C_heckbox with Frame (focusable)",
CanFocus = true,
X = Pos.Right (checkboxWithFrameLabel) + 1, Y = Pos.Top (checkboxWithFrameLabel), Width = 40, Height = 3,
BorderStyle = LineStyle.Dashed,
BorderStyle = LineStyle.Dashed
};
Top.Add (checkboxWithFrameFocusable);
app.Add (checkboxWithFrameFocusable);
checkboxWithFrameLabel = new Label { Text = "Checkb_ox with Frame:", X = 0, Y = Pos.Bottom (checkboxWithFrameFocusable) + 1 };
Top.Add (checkboxWithFrameLabel);
checkboxWithFrameLabel = new() { Text = "Checkb_ox with Frame:", X = 0, Y = Pos.Bottom (checkboxWithFrameFocusable) + 1 };
app.Add (checkboxWithFrameLabel);
var checkboxWithFrame = new CheckBox
{
@@ -126,14 +120,14 @@ public class HotKeys : Scenario
Title = "Checkbox with Frame (not focusable)",
X = Pos.Right (checkboxWithFrameLabel) + 1, Y = Pos.Top (checkboxWithFrameLabel), Width = 40, Height = 3,
CanFocus = false,
BorderStyle = LineStyle.Dashed,
BorderStyle = LineStyle.Dashed
};
Top.Add (checkboxWithFrame);
app.Add (checkboxWithFrame);
var button = new Button { X = Pos.Center (), Y = Pos.AnchorEnd (), Text = "_Press me!" };
app.Add (button);
var button = new Button { X = Pos.Center (), Y = Pos.AnchorEnd (1), Text = "_Press me!" };
Top.Add (button);
Application.Run (Top);
Application.Run (app);
app.Dispose ();
}
}

View File

@@ -8,21 +8,14 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("Proof of Concept")]
public class LineCanvasExperiment : Scenario
{
public override void Init ()
public override void Main ()
{
Application.Init ();
Top = new ();
}
/// <summary>Setup the scenario.</summary>
public override void Setup ()
{
//var menu = new MenuBar (new MenuBarItem [] {
//new MenuBarItem ("_File", new MenuItem [] {
// new MenuItem ("_Quit", "", () => Application.RequestStop()),
//}) });
//Top.Add (menu);
Window app = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
};
var frame1 = new FrameView
{
@@ -37,7 +30,7 @@ public class LineCanvasExperiment : Scenario
//View.Diagnostics ^= DiagnosticFlags.FrameRuler;
Top.Add (frame1);
app.Add (frame1);
var win1 = new Window
{
@@ -52,7 +45,7 @@ public class LineCanvasExperiment : Scenario
BorderStyle = LineStyle.Heavy,
SuperViewRendersLineCanvas = true
};
win1.Padding.Thickness = new Thickness (1);
win1.Padding.Thickness = new (1);
frame1.Add (win1);
@@ -140,9 +133,12 @@ public class LineCanvasExperiment : Scenario
SuperViewRendersLineCanvas = true
};
marginWindow.Margin.ColorScheme = Colors.ColorSchemes ["Dialog"];
marginWindow.Margin.Thickness = new Thickness (1);
marginWindow.Border.Thickness = new Thickness (1, 2, 1, 1);
marginWindow.Margin.Thickness = new (1);
marginWindow.Border.Thickness = new (1, 2, 1, 1);
frame1.Add (marginWindow);
Application.Run (app);
app.Dispose ();
}
}

View File

@@ -122,10 +122,10 @@ public class Scrolling : Scenario
);
// Demonstrate AnchorEnd - Button is anchored to bottom/right
var anchorButton = new Button { Y = Pos.AnchorEnd () - 1, Text = "Bottom Right" };
var anchorButton = new Button { Y = Pos.AnchorEnd (0) - 1, Text = "Bottom Right" };
// TODO: Use Pos.Width instead of (Right-Left) when implemented (#502)
anchorButton.X = Pos.AnchorEnd () - (Pos.Right (anchorButton) - Pos.Left (anchorButton));
anchorButton.X = Pos.AnchorEnd (0) - (Pos.Right (anchorButton) - Pos.Left (anchorButton));
anchorButton.Accept += (s, e) =>
{

View File

@@ -10,14 +10,21 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("Text and Formatting")]
public class TextAlignmentsAndDirections : Scenario
{
public override void Setup ()
public override void Main ()
{
Application.Init ();
Window app = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
};
// string txt = ".\n...\n.....\nHELLO\n.....\n...\n.";
// string txt = "┌──┴──┐\n┤HELLO├\n└──┬──┘";
var txt = "HELLO WORLD";
var color1 = new ColorScheme { Normal = new Attribute (Color.Black, Color.Gray) };
var color2 = new ColorScheme { Normal = new Attribute (Color.Black, Color.DarkGray) };
var color1 = new ColorScheme { Normal = new (Color.Black, Color.Gray) };
var color2 = new ColorScheme { Normal = new (Color.Black, Color.DarkGray) };
List<Label> txts = new (); // single line
List<Label> mtxts = new (); // multi line
@@ -125,14 +132,14 @@ public class TextAlignmentsAndDirections : Scenario
txts.Add (txtLabelHR);
txts.Add (txtLabelHJ);
Win.Add (labelHL);
Win.Add (txtLabelHL);
Win.Add (labelHC);
Win.Add (txtLabelHC);
Win.Add (labelHR);
Win.Add (txtLabelHR);
Win.Add (labelHJ);
Win.Add (txtLabelHJ);
app.Add (labelHL);
app.Add (txtLabelHL);
app.Add (labelHC);
app.Add (txtLabelHC);
app.Add (labelHR);
app.Add (txtLabelHR);
app.Add (labelHJ);
app.Add (txtLabelHJ);
// Vertical Single-Line
@@ -245,14 +252,14 @@ public class TextAlignmentsAndDirections : Scenario
txts.Add (txtLabelVB);
txts.Add (txtLabelVJ);
Win.Add (labelVT);
Win.Add (txtLabelVT);
Win.Add (labelVM);
Win.Add (txtLabelVM);
Win.Add (labelVB);
Win.Add (txtLabelVB);
Win.Add (labelVJ);
Win.Add (txtLabelVJ);
app.Add (labelVT);
app.Add (txtLabelVT);
app.Add (labelVM);
app.Add (txtLabelVM);
app.Add (labelVB);
app.Add (txtLabelVB);
app.Add (labelVJ);
app.Add (txtLabelVJ);
// Multi-Line
@@ -410,7 +417,7 @@ public class TextAlignmentsAndDirections : Scenario
container.Add (txtLabelBC);
container.Add (txtLabelBR);
Win.Add (container);
app.Add (container);
// Edit Text
@@ -437,7 +444,7 @@ public class TextAlignmentsAndDirections : Scenario
}
};
Win.KeyUp += (s, m) =>
app.KeyUp += (s, m) =>
{
foreach (Label v in txts)
{
@@ -452,7 +459,7 @@ public class TextAlignmentsAndDirections : Scenario
editText.SetFocus ();
Win.Add (editText);
app.Add (editText);
// JUSTIFY CHECKBOX
@@ -494,7 +501,7 @@ public class TextAlignmentsAndDirections : Scenario
}
};
Win.Add (justifyCheckbox);
app.Add (justifyCheckbox);
// Direction Options
@@ -518,6 +525,9 @@ public class TextAlignmentsAndDirections : Scenario
}
};
Win.Add (directionOptions);
app.Add (directionOptions);
Application.Run (app);
app.Dispose ();
}
}

View File

@@ -7,8 +7,15 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("Colors")]
public class TrueColors : Scenario
{
public override void Setup ()
public override void Main ()
{
Application.Init ();
Window app = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
};
var x = 2;
var y = 1;
@@ -18,7 +25,7 @@ public class TrueColors : Scenario
{
X = x, Y = y++, Text = $"Current driver is {Application.Driver.GetType ().Name}"
};
Win.Add (lblDriverName);
app.Add (lblDriverName);
y++;
var cbSupportsTrueColor = new CheckBox
@@ -29,7 +36,7 @@ public class TrueColors : Scenario
CanFocus = false,
Text = "Driver supports true color "
};
Win.Add (cbSupportsTrueColor);
app.Add (cbSupportsTrueColor);
var cbUseTrueColor = new CheckBox
{
@@ -40,53 +47,53 @@ public class TrueColors : Scenario
Text = "Force 16 colors"
};
cbUseTrueColor.Toggled += (_, evt) => { Application.Force16Colors = evt.NewValue ?? false; };
Win.Add (cbUseTrueColor);
app.Add (cbUseTrueColor);
y += 2;
SetupGradient ("Red gradient", x, ref y, i => new Color (i, 0));
SetupGradient ("Green gradient", x, ref y, i => new Color (0, i));
SetupGradient ("Blue gradient", x, ref y, i => new Color (0, 0, i));
SetupGradient ("Yellow gradient", x, ref y, i => new Color (i, i));
SetupGradient ("Magenta gradient", x, ref y, i => new Color (i, 0, i));
SetupGradient ("Cyan gradient", x, ref y, i => new Color (0, i, i));
SetupGradient ("Gray gradient", x, ref y, i => new Color (i, i, i));
SetupGradient ("Red gradient", x, ref y, i => new (i, 0));
SetupGradient ("Green gradient", x, ref y, i => new (0, i));
SetupGradient ("Blue gradient", x, ref y, i => new (0, 0, i));
SetupGradient ("Yellow gradient", x, ref y, i => new (i, i));
SetupGradient ("Magenta gradient", x, ref y, i => new (i, 0, i));
SetupGradient ("Cyan gradient", x, ref y, i => new (0, i, i));
SetupGradient ("Gray gradient", x, ref y, i => new (i, i, i));
Win.Add (
app.Add (
new Label { X = Pos.AnchorEnd (44), Y = 2, Text = "Mouse over to get the gradient view color:" }
);
Win.Add (
app.Add (
new Label { X = Pos.AnchorEnd (44), Y = 4, Text = "Red:" }
);
Win.Add (
app.Add (
new Label { X = Pos.AnchorEnd (44), Y = 5, Text = "Green:" }
);
Win.Add (
app.Add (
new Label { X = Pos.AnchorEnd (44), Y = 6, Text = "Blue:" }
);
Win.Add (
app.Add (
new Label { X = Pos.AnchorEnd (44), Y = 8, Text = "Darker:" }
);
Win.Add (
app.Add (
new Label { X = Pos.AnchorEnd (44), Y = 9, Text = "Lighter:" }
);
var lblRed = new Label { X = Pos.AnchorEnd (32), Y = 4, Text = "na" };
Win.Add (lblRed);
app.Add (lblRed);
var lblGreen = new Label { X = Pos.AnchorEnd (32), Y = 5, Text = "na" };
Win.Add (lblGreen);
app.Add (lblGreen);
var lblBlue = new Label { X = Pos.AnchorEnd (32), Y = 6, Text = "na" };
Win.Add (lblBlue);
app.Add (lblBlue);
var lblDarker = new Label { X = Pos.AnchorEnd (32), Y = 8, Text = " " };
Win.Add (lblDarker);
app.Add (lblDarker);
var lblLighter = new Label { X = Pos.AnchorEnd (32), Y = 9, Text = " " };
Win.Add (lblLighter);
app.Add (lblLighter);
Application.MouseEvent += (s, e) =>
{
@@ -94,16 +101,17 @@ public class TrueColors : Scenario
{
return;
}
if (e.Flags == MouseFlags.Button1Clicked)
{
Attribute normal = e.View.GetNormalColor ();
lblLighter.ColorScheme = new ColorScheme(e.View.ColorScheme)
lblLighter.ColorScheme = new (e.View.ColorScheme)
{
Normal = new Attribute (
normal.Foreground,
normal.Background.GetHighlightColor ()
)
Normal = new (
normal.Foreground,
normal.Background.GetHighlightColor ()
)
};
}
else
@@ -114,31 +122,35 @@ public class TrueColors : Scenario
lblBlue.Text = normal.Foreground.B.ToString ();
}
};
}
Application.Run (app);
app.Dispose ();
private void SetupGradient (string name, int x, ref int y, Func<int, Color> colorFunc)
{
var gradient = new Label { X = x, Y = y++, Text = name };
Win.Add (gradient);
return;
for (int dx = x, i = 0; i <= 256; i += 4)
void SetupGradient (string name, int x, ref int y, Func<int, Color> colorFunc)
{
var l = new Label
{
X = dx++,
Y = y,
ColorScheme = new ColorScheme
{
Normal = new Attribute (
colorFunc (Math.Clamp (i, 0, 255)),
colorFunc (Math.Clamp (i, 0, 255))
)
},
Text = " "
};
Win.Add (l);
}
var gradient = new Label { X = x, Y = y++, Text = name };
app.Add (gradient);
y += 2;
for (int dx = x, i = 0; i <= 256; i += 4)
{
var l = new Label
{
X = dx++,
Y = y,
ColorScheme = new()
{
Normal = new (
colorFunc (Math.Clamp (i, 0, 255)),
colorFunc (Math.Clamp (i, 0, 255))
)
},
Text = " "
};
app.Add (l);
}
y += 2;
}
}
}

View File

@@ -9,17 +9,15 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("Proof of Concept")]
public class ViewExperiments : Scenario
{
public override void Init ()
public override void Main ()
{
Application.Init ();
ConfigurationManager.Themes.Theme = Theme;
ConfigurationManager.Apply ();
Top = new ();
Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
}
public override void Setup ()
{
Window app = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
};
var containerLabel = new Label
{
X = 0,
@@ -28,7 +26,7 @@ public class ViewExperiments : Scenario
Width = Dim.Fill (),
Height = 3
};
Top.Add (containerLabel);
app.Add (containerLabel);
var view = new View
{
@@ -41,7 +39,7 @@ public class ViewExperiments : Scenario
Id = "DaView"
};
//Top.Add (view);
//app.Add (view);
view.Margin.Thickness = new Thickness (2, 2, 2, 2);
view.Margin.ColorScheme = Colors.ColorSchemes ["Toplevel"];
@@ -199,8 +197,7 @@ public class ViewExperiments : Scenario
};
view.Add (edit);
edit = new TextField { Text = "AnchorEnd[Right];AnchorEnd (1)", Y = Pos.AnchorEnd (1), Width = 30, Height = 1 };
edit.X = Pos.AnchorEnd () - (Pos.Right (edit) - Pos.Left (edit));
edit = new TextField { Text = "AnchorEnd ();AnchorEnd ()", X = Pos.AnchorEnd(), Y = Pos.AnchorEnd (), Width = 30, Height = 1 };
view.Add (edit);
edit = new TextField
@@ -217,9 +214,9 @@ public class ViewExperiments : Scenario
{
containerLabel.Text =
$"Container.Frame: {
Top.Frame
app.Frame
} .Bounds: {
Top.Viewport
app.Viewport
}\nView.Frame: {
view.Frame
} .Viewport: {
@@ -244,11 +241,14 @@ public class ViewExperiments : Scenario
ViewToEdit = view
};
Top.Add (editor);
app.Add (editor);
view.X = 36;
view.Y = 4;
view.Width = Dim.Fill ();
view.Height = Dim.Fill ();
Top.Add (view);
app.Add (view);
Application.Run (app);
app.Dispose ();
}
}

View File

@@ -8,8 +8,15 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("Layout")]
public class WindowsAndFrameViews : Scenario
{
public override void Setup ()
public override void Main ()
{
Application.Init ();
Window app = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
};
static int About ()
{
return MessageBox.Query (
@@ -26,11 +33,7 @@ public class WindowsAndFrameViews : Scenario
// list of Windows we create
List<View> listWin = new ();
//Ignore the Win that UI Catalog created and create a new one
Top.Remove (Win);
Win?.Dispose ();
Win = new Window
var win = new Window
{
Title = $"{listWin.Count} - Scenario: {GetName ()}",
X = Pos.Center (),
@@ -39,8 +42,8 @@ public class WindowsAndFrameViews : Scenario
Height = 10,
ColorScheme = Colors.ColorSchemes ["Dialog"]
};
Win.Padding.Thickness = new Thickness (padding);
Win.Margin.Thickness = new Thickness (margin);
win.Padding.Thickness = new (padding);
win.Margin.Thickness = new (margin);
var paddingButton = new Button
{
@@ -50,21 +53,21 @@ public class WindowsAndFrameViews : Scenario
Text = $"Padding of container is {padding}"
};
paddingButton.Accept += (s, e) => About ();
Win.Add (paddingButton);
win.Add (paddingButton);
Win.Add (
win.Add (
new Button
{
X = Pos.Center (),
Y = Pos.AnchorEnd (1),
Y = Pos.AnchorEnd (),
ColorScheme = Colors.ColorSchemes ["Error"],
Text = "Press ME! (Y = Pos.AnchorEnd(1))"
}
);
Top.Add (Win);
app.Add (win);
// add it to our list
listWin.Add (Win);
listWin.Add (win);
// create 3 more Windows in a loop, adding them Application.Top
// Each with a
@@ -75,9 +78,9 @@ public class WindowsAndFrameViews : Scenario
//
for (var pad = 0; pad < 3; pad++)
{
Window win = null;
Window loopWin = null;
win = new Window
loopWin = new()
{
Title = $"{listWin.Count} - Window Loop - padding = {pad}",
X = margin,
@@ -85,9 +88,9 @@ public class WindowsAndFrameViews : Scenario
Width = Dim.Fill (margin),
Height = contentHeight + pad * 2 + 2
};
win.Padding.Thickness = new Thickness (pad);
loopWin.Padding.Thickness = new (pad);
win.ColorScheme = Colors.ColorSchemes ["Dialog"];
loopWin.ColorScheme = Colors.ColorSchemes ["Dialog"];
var pressMeButton = new Button
{
@@ -95,8 +98,8 @@ public class WindowsAndFrameViews : Scenario
};
pressMeButton.Accept += (s, e) =>
MessageBox.ErrorQuery (win.Title, "Neat?", "Yes", "No");
win.Add (pressMeButton);
MessageBox.ErrorQuery (loopWin.Title, "Neat?", "Yes", "No");
loopWin.Add (pressMeButton);
var subWin = new Window
{
@@ -110,9 +113,9 @@ public class WindowsAndFrameViews : Scenario
};
subWin.Add (
new TextField { Y = 1, ColorScheme = Colors.ColorSchemes ["Error"], Text = "Edit me! " + win.Title }
new TextField { Y = 1, ColorScheme = Colors.ColorSchemes ["Error"], Text = "Edit me! " + loopWin.Title }
);
win.Add (subWin);
loopWin.Add (subWin);
var frameView = new FrameView
{
@@ -128,27 +131,15 @@ public class WindowsAndFrameViews : Scenario
frameView.Add (
new TextField { Y = 1, Text = "Edit Me!" }
);
win.Add (frameView);
loopWin.Add (frameView);
Top.Add (win);
listWin.Add (win);
app.Add (loopWin);
listWin.Add (loopWin);
}
// Add a FrameView (frame) to Application.Top
// Position it at Bottom, using the list of Windows we created above.
// Fill it with
// a label
// a SubWindow containing (subWinofFV)
// a TextField
// two checkboxes
// a Sub FrameView containing (subFrameViewofFV)
// a TextField
// two CheckBoxes
// a checkbox
// a checkbox
FrameView frame = null;
frame = new FrameView
frame = new()
{
X = margin,
Y = Pos.Bottom (listWin.Last ()) + margin / 2,
@@ -199,22 +190,22 @@ public class WindowsAndFrameViews : Scenario
subFrameViewofFV.Add (new CheckBox { Y = 1, Text = "Check me" });
// BUGBUG: This checkbox is not shown even though frameViewFV has 3 rows in
// its client area. #522
subFrameViewofFV.Add (new CheckBox { Y = 2, Text = "Or, Check me" });
frame.Add (
new CheckBox { X = 0, Y = Pos.AnchorEnd (1), Text = "Btn1 (Y = Pos.AnchorEnd (1))" }
new CheckBox { X = 0, Y = Pos.AnchorEnd (), Text = "Btn1 (Y = Pos.AnchorEnd ())" }
);
var c = new CheckBox { Y = Pos.AnchorEnd (1), Text = "Btn2 (Y = Pos.AnchorEnd (1))" };
c.X = Pos.AnchorEnd () - (Pos.Right (c) - Pos.Left (c));
var c = new CheckBox { X = Pos.AnchorEnd (), Y = Pos.AnchorEnd (), Text = "Btn2 (Y = Pos.AnchorEnd ())" };
frame.Add (c);
frame.Add (subFrameViewofFV);
Top.Add (frame);
app.Add (frame);
listWin.Add (frame);
Top.ColorScheme = Colors.ColorSchemes ["Base"];
app.ColorScheme = Colors.ColorSchemes ["Base"];
Application.Run (app);
app.Dispose ();
}
}

View File

@@ -0,0 +1,297 @@
using Xunit.Abstractions;
using static Terminal.Gui.Pos;
namespace Terminal.Gui.ViewTests;
public class AnchorEndTests (ITestOutputHelper output)
{
[Fact]
public void PosAnchorEnd_Constructor ()
{
var posAnchorEnd = new PosAnchorEnd (10);
Assert.NotNull (posAnchorEnd);
}
[Theory]
[InlineData (0, 0, true)]
[InlineData (10, 10, true)]
[InlineData (0, 10, false)]
[InlineData (10, 1, false)]
public void PosAnchorEnd_Equals (int offset1, int offset2, bool expectedEquals)
{
var posAnchorEnd1 = new PosAnchorEnd (offset1);
var posAnchorEnd2 = new PosAnchorEnd (offset2);
Assert.Equal (expectedEquals, posAnchorEnd1.Equals (posAnchorEnd2));
Assert.Equal (expectedEquals, posAnchorEnd2.Equals (posAnchorEnd1));
}
[Fact]
public void PosAnchorEnd_GetHashCode ()
{
var posAnchorEnd = new PosAnchorEnd (10);
var expectedHashCode = 10.GetHashCode ();
Assert.Equal (expectedHashCode, posAnchorEnd.GetHashCode ());
}
[Fact]
public void PosAnchorEnd_ToString ()
{
var posAnchorEnd = new PosAnchorEnd (10);
var expectedString = "AnchorEnd(10)";
Assert.Equal (expectedString, posAnchorEnd.ToString ());
}
[Fact]
public void PosAnchorEnd_Anchor ()
{
var posAnchorEnd = new PosAnchorEnd (10);
var width = 50;
var expectedAnchor = width - 10;
Assert.Equal (expectedAnchor, posAnchorEnd.Anchor (width));
}
[Fact]
public void AnchorEnd_CreatesCorrectInstance ()
{
var pos = Pos.AnchorEnd (10);
Assert.IsType<PosAnchorEnd> (pos);
}
[Fact]
public void AnchorEnd_Negative_Throws ()
{
Pos pos;
int n = -1;
Assert.Throws<ArgumentException> (() => pos = Pos.AnchorEnd (n));
}
[Theory]
[InlineData (0)]
[InlineData (1)]
public void AnchorEnd_SetsValue_Anchor_Is_Negative (int offset)
{
Pos pos = Pos.AnchorEnd (offset);
Assert.Equal (offset, -pos.Anchor (0));
}
[Theory]
[InlineData (0, 0, 25)]
[InlineData (0, 10, 25)]
[InlineData (1, 10, 24)]
[InlineData (10, 10, 15)]
[InlineData (20, 10, 5)]
[InlineData (25, 10, 0)]
[InlineData (26, 10, -1)]
public void AnchorEnd_With_Offset_PositionsViewOffsetFromRight (int offset, int width, int expectedXPosition)
{
// Arrange
var superView = new View { Width = 25, Height = 25 };
var view = new View
{
X = Pos.AnchorEnd (offset),
Width = width,
Height = 1
};
superView.Add (view);
superView.BeginInit ();
superView.EndInit ();
// Act
superView.LayoutSubviews ();
// Assert
Assert.Equal (expectedXPosition, view.Frame.X);
}
// UseDimForOffset tests
[Fact]
public void AnchorEnd_UseDimForOffset_CreatesCorrectInstance ()
{
var pos = Pos.AnchorEnd ();
Assert.IsType<PosAnchorEnd> (pos);
Assert.True (((PosAnchorEnd)pos).UseDimForOffset);
}
[Fact]
public void AnchorEnd_UseDimForOffset_SetsValue_Anchor_Is_Negative ()
{
Pos pos = Pos.AnchorEnd ();
Assert.Equal (-10, -pos.Anchor (10));
}
[Theory]
[InlineData (0, 25)]
[InlineData (10, 15)]
[InlineData (9, 16)]
[InlineData (11, 14)]
[InlineData (25, 0)]
[InlineData (26, -1)]
public void AnchorEnd_UseDimForOffset_PositionsViewOffsetByDim (int dim, int expectedXPosition)
{
// Arrange
var superView = new View { Width = 25, Height = 25 };
var view = new View
{
X = Pos.AnchorEnd (),
Width = dim,
Height = 1
};
superView.Add (view);
superView.BeginInit ();
superView.EndInit ();
// Act
superView.LayoutSubviews ();
// Assert
Assert.Equal (expectedXPosition, view.Frame.X);
}
[Theory]
[InlineData (0, 25)]
[InlineData (10, 23)]
[InlineData (50, 13)]
[InlineData (100, 0)]
public void AnchorEnd_UseDimForOffset_DimPercent_PositionsViewOffsetByDim (int percent, int expectedXPosition)
{
// Arrange
var superView = new View { Width = 25, Height = 25 };
var view = new View
{
X = Pos.AnchorEnd (),
Width = Dim.Percent ( percent),
Height = 1
};
superView.Add (view);
superView.BeginInit ();
superView.EndInit ();
// Act
superView.LayoutSubviews ();
// Assert
Assert.Equal (expectedXPosition, view.Frame.X);
}
// This test used to be Dialog_In_Window_With_TextField_And_Button_AnchorEnd in DialogTests.
[Fact]
[SetupFakeDriver]
public void AnchorEnd_View_And_Button ()
{
((FakeDriver)Application.Driver).SetBufferSize (20, 5);
var b = $"{CM.Glyphs.LeftBracket} Ok {CM.Glyphs.RightBracket}";
var frame = new FrameView { Width = 18, Height = 3 };
Assert.Equal (16, frame.Viewport.Width);
Button btn = null;
int Btn_Width () { return btn?.Viewport.Width ?? 0; }
btn = new () { Text = "Ok", X = Pos.AnchorEnd (0) - Pos.Function (Btn_Width) };
var view = new View
{
Text = "0123456789abcdefghij",
// Dim.Fill (1) fills remaining space minus 1 (16 - 1 = 15)
// Dim.Function (Btn_Width) is 6
// Width should be 15 - 6 = 9
Width = Dim.Fill (1) - Dim.Function (Btn_Width),
Height = 1
};
frame.Add (btn, view);
frame.BeginInit ();
frame.EndInit ();
frame.Draw ();
Assert.Equal (6, btn.Viewport.Width);
Assert.Equal (10, btn.Frame.X); // frame.Viewport.Width (16) - btn.Frame.Width (6) = 10
Assert.Equal (0, btn.Frame.Y);
Assert.Equal (6, btn.Frame.Width);
Assert.Equal (1, btn.Frame.Height);
Assert.Equal (9, view.Viewport.Width); // frame.Viewport.Width (16) - Dim.Fill (1) - Dim.Function (6) = 9
Assert.Equal (0, view.Frame.X);
Assert.Equal (0, view.Frame.Y);
Assert.Equal (9, view.Frame.Width);
Assert.Equal (1, view.Frame.Height);
var expected = $@"
┌────────────────┐
│012345678 {b}│
└────────────────┘
";
_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
}
// 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 AnchorEnd_Equal_Inside_Window ()
{
var viewWidth = 10;
var viewHeight = 1;
var tv = new TextView
{
X = Pos.AnchorEnd (viewWidth), Y = Pos.AnchorEnd (viewHeight), Width = viewWidth, Height = viewHeight
};
var win = new Window ();
win.Add (tv);
Toplevel top = new ();
top.Add (win);
RunState rs = Application.Begin (top);
Assert.Equal (new (0, 0, 80, 25), top.Frame);
Assert.Equal (new (0, 0, 80, 25), win.Frame);
Assert.Equal (new (68, 22, 10, 1), tv.Frame);
Application.End (rs);
}
// 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 AnchorEnd_Equal_Inside_Window_With_MenuBar_And_StatusBar_On_Toplevel ()
{
var viewWidth = 10;
var viewHeight = 1;
var tv = new TextView
{
X = Pos.AnchorEnd (viewWidth), Y = Pos.AnchorEnd (viewHeight), Width = viewWidth, Height = viewHeight
};
var win = new Window ();
win.Add (tv);
var menu = new MenuBar ();
var status = new StatusBar ();
Toplevel top = new ();
top.Add (win, menu, status);
RunState rs = Application.Begin (top);
Assert.Equal (new (0, 0, 80, 25), top.Frame);
Assert.Equal (new (0, 0, 80, 1), menu.Frame);
Assert.Equal (new (0, 24, 80, 1), status.Frame);
Assert.Equal (new (0, 1, 80, 23), win.Frame);
Assert.Equal (new (68, 20, 10, 1), tv.Frame);
Application.End (rs);
}
}

View File

@@ -1,6 +1,8 @@
using System.Globalization;
using System.Text;
using Xunit.Abstractions;
using static Terminal.Gui.Dim;
// Alias Console to MockConsole so we don't accidentally use Console
using Console = Terminal.Gui.FakeConsole;
@@ -22,6 +24,57 @@ public class DimTests
Thread.CurrentThread.CurrentUICulture = culture;
}
[Fact]
public void DimAbsolute_GetDimension_ReturnsCorrectValue ()
{
var dim = new DimAbsolute (10);
var result = dim.Calculate (0, 100, 50, false);
Assert.Equal (10, result);
}
[Fact]
public void DimCombine_GetDimension_ReturnsCorrectValue ()
{
var dim1 = new DimAbsolute (10);
var dim2 = new DimAbsolute (20);
var dim = dim1 + dim2;
var result = dim.Calculate (0, 100, 50, false);
Assert.Equal (30, result);
}
[Fact]
public void DimFactor_GetDimension_ReturnsCorrectValue ()
{
var dim = new DimFactor (0.5f);
var result = dim.Calculate (0, 100, 50, false);
Assert.Equal (50, result);
}
[Fact]
public void DimFill_GetDimension_ReturnsCorrectValue ()
{
var dim = Dim.Fill ();
var result = dim.Calculate (0, 100, 50, false);
Assert.Equal (100, result);
}
[Fact]
public void DimFunc_GetDimension_ReturnsCorrectValue ()
{
var dim = new DimFunc (() => 10);
var result = dim.Calculate (0, 100, 50, false);
Assert.Equal (10, result);
}
[Fact]
public void DimView_GetDimension_ReturnsCorrectValue ()
{
var view = new View { Width = 10 };
var dim = new DimView (view, Dimension.Width);
var result = dim.Calculate (0, 100, 50, false);
Assert.Equal (10, result);
}
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
// A new test that does not depend on Application is needed.
[Fact]
@@ -508,9 +561,9 @@ public class DimTests
Assert.Equal (20, dimCombine.Anchor (100));
var view = new View { Frame = new Rectangle (20, 10, 20, 1) };
var dimViewHeight = new Dim.DimView (view, 0);
var dimViewHeight = new Dim.DimView (view, Dimension.Height);
Assert.Equal (1, dimViewHeight.Anchor (0));
var dimViewWidth = new Dim.DimView (view, 1);
var dimViewWidth = new Dim.DimView (view, Dimension.Width);
Assert.Equal (20, dimViewWidth.Anchor (0));
view.Dispose ();

View File

@@ -1,165 +1,65 @@
using Xunit.Abstractions;
// Alias Console to MockConsole so we don't accidentally use Console
using static Terminal.Gui.Dim;
using static Terminal.Gui.Pos;
namespace Terminal.Gui.ViewTests;
public class PosTests
public class PosTests (ITestOutputHelper output)
{
private readonly ITestOutputHelper _output;
public PosTests (ITestOutputHelper output) { _output = output; }
[Fact]
public void AnchorEnd_Equal ()
public void PosAbsolute_GetLocation_ReturnsExpectedValue ()
{
var n1 = 0;
var n2 = 0;
Pos pos1 = Pos.AnchorEnd (n1);
Pos pos2 = Pos.AnchorEnd (n2);
Assert.Equal (pos1, pos2);
// Test inequality
n2 = 5;
pos2 = Pos.AnchorEnd (n2);
Assert.NotEqual (pos1, pos2);
}
// 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 AnchorEnd_Equal_Inside_Window ()
{
var viewWidth = 10;
var viewHeight = 1;
var tv = new TextView
{
X = Pos.AnchorEnd (viewWidth), Y = Pos.AnchorEnd (viewHeight), Width = viewWidth, Height = viewHeight
};
var win = new Window ();
win.Add (tv);
Toplevel top = new ();
top.Add (win);
RunState rs = Application.Begin (top);
Assert.Equal (new Rectangle (0, 0, 80, 25), top.Frame);
Assert.Equal (new Rectangle (0, 0, 80, 25), win.Frame);
Assert.Equal (new Rectangle (68, 22, 10, 1), tv.Frame);
Application.End (rs);
}
// 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 AnchorEnd_Equal_Inside_Window_With_MenuBar_And_StatusBar_On_Toplevel ()
{
var viewWidth = 10;
var viewHeight = 1;
var tv = new TextView
{
X = Pos.AnchorEnd (viewWidth), Y = Pos.AnchorEnd (viewHeight), Width = viewWidth, Height = viewHeight
};
var win = new Window ();
win.Add (tv);
var menu = new MenuBar ();
var status = new StatusBar ();
Toplevel top = new ();
top.Add (win, menu, status);
RunState rs = Application.Begin (top);
Assert.Equal (new Rectangle (0, 0, 80, 25), top.Frame);
Assert.Equal (new Rectangle (0, 0, 80, 1), menu.Frame);
Assert.Equal (new Rectangle (0, 24, 80, 1), status.Frame);
Assert.Equal (new Rectangle (0, 1, 80, 23), win.Frame);
Assert.Equal (new Rectangle (68, 20, 10, 1), tv.Frame);
Application.End (rs);
var posAbsolute = new PosAbsolute (5);
var result = posAbsolute.Calculate (10, new DimAbsolute (2), 1, false);
Assert.Equal (5, result);
}
[Fact]
public void AnchorEnd_Negative_Throws ()
public void PosAnchorEnd_GetLocation_ReturnsExpectedValue ()
{
Pos pos;
int n = -1;
Assert.Throws<ArgumentException> (() => pos = Pos.AnchorEnd (n));
var posAnchorEnd = new PosAnchorEnd (5);
var result = posAnchorEnd.Calculate (10, new DimAbsolute (2), 1, false);
Assert.Equal (5, result);
}
[Fact]
public void AnchorEnd_SetsValue ()
public void PosCenter_GetLocation_ReturnsExpectedValue ()
{
var n = 0;
Pos pos = Pos.AnchorEnd ();
Assert.Equal ($"AnchorEnd({n})", pos.ToString ());
n = 5;
pos = Pos.AnchorEnd (n);
Assert.Equal ($"AnchorEnd({n})", pos.ToString ());
var posCenter = new PosCenter ();
var result = posCenter.Calculate (10, new DimAbsolute (2), 1, false);
Assert.Equal (4, result);
}
// This test used to be Dialog_In_Window_With_TextField_And_Button_AnchorEnd in DialogTests.
[Fact]
[SetupFakeDriver]
public void AnchorEnd_View_And_Button ()
public void PosCombine_GetLocation_ReturnsExpectedValue ()
{
((FakeDriver)Application.Driver).SetBufferSize (20, 5);
var posCombine = new PosCombine (true, new PosAbsolute (5), new PosAbsolute (3));
var result = posCombine.Calculate (10, new DimAbsolute (2), 1, false);
Assert.Equal (8, result);
}
var b = $"{CM.Glyphs.LeftBracket} Ok {CM.Glyphs.RightBracket}";
[Fact]
public void PosFactor_GetLocation_ReturnsExpectedValue ()
{
var posFactor = new PosFactor (0.5f);
var result = posFactor.Calculate (10, new DimAbsolute (2), 1, false);
Assert.Equal (5, result);
}
var frame = new FrameView { Width = 18, Height = 3 };
Assert.Equal (16, frame.Viewport.Width);
[Fact]
public void PosFunc_GetLocation_ReturnsExpectedValue ()
{
var posFunc = new PosFunc (() => 5);
var result = posFunc.Calculate (10, new DimAbsolute (2), 1, false);
Assert.Equal (5, result);
}
Button btn = null;
int Btn_Width () { return btn?.Viewport.Width ?? 0; }
btn = new Button { Text = "Ok", X = Pos.AnchorEnd () - Pos.Function (Btn_Width) };
var view = new View
{
Text = "0123456789abcdefghij",
// Dim.Fill (1) fills remaining space minus 1 (16 - 1 = 15)
// Dim.Function (Btn_Width) is 6
// Width should be 15 - 6 = 9
Width = Dim.Fill (1) - Dim.Function (Btn_Width),
Height = 1
};
frame.Add (btn, view);
frame.BeginInit ();
frame.EndInit ();
frame.Draw ();
Assert.Equal (6, btn.Viewport.Width);
Assert.Equal (10, btn.Frame.X); // frame.Viewport.Width (16) - btn.Frame.Width (6) = 10
Assert.Equal (0, btn.Frame.Y);
Assert.Equal (6, btn.Frame.Width);
Assert.Equal (1, btn.Frame.Height);
Assert.Equal (9, view.Viewport.Width); // frame.Viewport.Width (16) - Dim.Fill (1) - Dim.Function (6) = 9
Assert.Equal (0, view.Frame.X);
Assert.Equal (0, view.Frame.Y);
Assert.Equal (9, view.Frame.Width);
Assert.Equal (1, view.Frame.Height);
var expected = $@"
┌────────────────┐
│012345678 {
b
}│
└────────────────┘
";
_ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
[Fact]
public void PosView_GetLocation_ReturnsExpectedValue ()
{
var posView = new PosView (new View { Frame = new Rectangle (5, 5, 10, 10) }, 0);
var result = posView.Calculate (10, new DimAbsolute (2), 1, false);
Assert.Equal (5, result);
}
[Fact]
@@ -201,28 +101,28 @@ public class PosTests
Pos pos = Pos.Left (v);
Assert.Equal (
$"View(side=x,target=View(V){v.Frame})",
$"View(side=left,target=View(V){v.Frame})",
pos.ToString ()
);
pos = Pos.X (v);
Assert.Equal (
$"View(side=x,target=View(V){v.Frame})",
$"View(side=left,target=View(V){v.Frame})",
pos.ToString ()
);
pos = Pos.Top (v);
Assert.Equal (
$"View(side=y,target=View(V){v.Frame})",
$"View(side=top,target=View(V){v.Frame})",
pos.ToString ()
);
pos = Pos.Y (v);
Assert.Equal (
$"View(side=y,target=View(V){v.Frame})",
$"View(side=top,target=View(V){v.Frame})",
pos.ToString ()
);
@@ -291,19 +191,19 @@ public class PosTests
Assert.Equal (posCombine._right, posAbsolute);
Assert.Equal (20, posCombine.Anchor (100));
posCombine = new Pos.PosCombine (true, posAbsolute, posFactor);
posCombine = new (true, posAbsolute, posFactor);
Assert.Equal (posCombine._left, posAbsolute);
Assert.Equal (posCombine._right, posFactor);
Assert.Equal (20, posCombine.Anchor (100));
var view = new View { Frame = new Rectangle (20, 10, 20, 1) };
var posViewX = new Pos.PosView (view, 0);
var view = new View { Frame = new (20, 10, 20, 1) };
var posViewX = new Pos.PosView (view, Pos.Side.Left);
Assert.Equal (20, posViewX.Anchor (0));
var posViewY = new Pos.PosView (view, 1);
var posViewY = new Pos.PosView (view, Pos.Side.Top);
Assert.Equal (10, posViewY.Anchor (0));
var posRight = new Pos.PosView (view, 2);
var posRight = new Pos.PosView (view, Pos.Side.Right);
Assert.Equal (40, posRight.Anchor (0));
var posViewBottom = new Pos.PosView (view, 3);
var posViewBottom = new Pos.PosView (view, Pos.Side.Bottom);
Assert.Equal (11, posViewBottom.Anchor (0));
view.Dispose ();
@@ -339,6 +239,7 @@ public class PosTests
Application.End (rs);
Application.Top.Dispose ();
// Shutdown must be called to safely clean up Application if Init has been called
Application.Shutdown ();
}
@@ -513,6 +414,7 @@ public class PosTests
Assert.Equal (20, count);
top.Dispose ();
// Shutdown must be called to safely clean up Application if Init has been called
Application.Shutdown ();
}
@@ -582,6 +484,7 @@ public class PosTests
Assert.Equal (0, count);
top.Dispose ();
// Shutdown must be called to safely clean up Application if Init has been called
Application.Shutdown ();
}
@@ -619,7 +522,7 @@ public class PosTests
var super = new View { Width = 10, Height = 10, Text = "super" };
var view1 = new View { Width = 2, Height = 2, Text = "view1" };
var view2 = new View { Width = 2, Height = 2, Text = "view2" };
view2.X = Pos.AnchorEnd () - (Pos.Right (view2) - Pos.Left (view2));
view2.X = Pos.AnchorEnd (0) - (Pos.Right (view2) - Pos.Left (view2));
super.Add (view1, view2);
super.BeginInit ();
@@ -627,9 +530,9 @@ public class PosTests
Exception exception = Record.Exception (super.LayoutSubviews);
Assert.Null (exception);
Assert.Equal (new Rectangle (0, 0, 10, 10), super.Frame);
Assert.Equal (new Rectangle (0, 0, 2, 2), view1.Frame);
Assert.Equal (new Rectangle (8, 0, 2, 2), view2.Frame);
Assert.Equal (new (0, 0, 10, 10), super.Frame);
Assert.Equal (new (0, 0, 2, 2), view1.Frame);
Assert.Equal (new (8, 0, 2, 2), view2.Frame);
super.Dispose ();
}
@@ -714,21 +617,21 @@ public class PosTests
Pos pos;
// Pos.Left
side = "x";
side = "left";
testInt = 0;
testRect = Rectangle.Empty;
pos = Pos.Left (new View ());
pos = Pos.Left (new ());
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
pos = Pos.Left (new View { Frame = testRect });
pos = Pos.Left (new() { Frame = testRect });
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
testRect = new Rectangle (1, 2, 3, 4);
pos = Pos.Left (new View { Frame = testRect });
testRect = new (1, 2, 3, 4);
pos = Pos.Left (new() { Frame = testRect });
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
// Pos.Left(win) + 0
pos = Pos.Left (new View { Frame = testRect }) + testInt;
pos = Pos.Left (new() { Frame = testRect }) + testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -738,7 +641,7 @@ public class PosTests
testInt = 1;
// Pos.Left(win) +1
pos = Pos.Left (new View { Frame = testRect }) + testInt;
pos = Pos.Left (new() { Frame = testRect }) + testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -748,7 +651,7 @@ public class PosTests
testInt = -1;
// Pos.Left(win) -1
pos = Pos.Left (new View { Frame = testRect }) - testInt;
pos = Pos.Left (new() { Frame = testRect }) - testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -756,21 +659,21 @@ public class PosTests
);
// Pos.X
side = "x";
side = "left";
testInt = 0;
testRect = Rectangle.Empty;
pos = Pos.X (new View ());
pos = Pos.X (new ());
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
pos = Pos.X (new View { Frame = testRect });
pos = Pos.X (new() { Frame = testRect });
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
testRect = new Rectangle (1, 2, 3, 4);
pos = Pos.X (new View { Frame = testRect });
testRect = new (1, 2, 3, 4);
pos = Pos.X (new() { Frame = testRect });
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
// Pos.X(win) + 0
pos = Pos.X (new View { Frame = testRect }) + testInt;
pos = Pos.X (new() { Frame = testRect }) + testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -780,7 +683,7 @@ public class PosTests
testInt = 1;
// Pos.X(win) +1
pos = Pos.X (new View { Frame = testRect }) + testInt;
pos = Pos.X (new() { Frame = testRect }) + testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -790,7 +693,7 @@ public class PosTests
testInt = -1;
// Pos.X(win) -1
pos = Pos.X (new View { Frame = testRect }) - testInt;
pos = Pos.X (new() { Frame = testRect }) - testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -798,21 +701,21 @@ public class PosTests
);
// Pos.Top
side = "y";
side = "top";
testInt = 0;
testRect = Rectangle.Empty;
pos = Pos.Top (new View ());
pos = Pos.Top (new ());
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
pos = Pos.Top (new View { Frame = testRect });
pos = Pos.Top (new() { Frame = testRect });
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
testRect = new Rectangle (1, 2, 3, 4);
pos = Pos.Top (new View { Frame = testRect });
testRect = new (1, 2, 3, 4);
pos = Pos.Top (new() { Frame = testRect });
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
// Pos.Top(win) + 0
pos = Pos.Top (new View { Frame = testRect }) + testInt;
pos = Pos.Top (new() { Frame = testRect }) + testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -822,7 +725,7 @@ public class PosTests
testInt = 1;
// Pos.Top(win) +1
pos = Pos.Top (new View { Frame = testRect }) + testInt;
pos = Pos.Top (new() { Frame = testRect }) + testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -832,7 +735,7 @@ public class PosTests
testInt = -1;
// Pos.Top(win) -1
pos = Pos.Top (new View { Frame = testRect }) - testInt;
pos = Pos.Top (new() { Frame = testRect }) - testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -840,21 +743,21 @@ public class PosTests
);
// Pos.Y
side = "y";
side = "top";
testInt = 0;
testRect = Rectangle.Empty;
pos = Pos.Y (new View ());
pos = Pos.Y (new ());
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
pos = Pos.Y (new View { Frame = testRect });
pos = Pos.Y (new() { Frame = testRect });
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
testRect = new Rectangle (1, 2, 3, 4);
pos = Pos.Y (new View { Frame = testRect });
testRect = new (1, 2, 3, 4);
pos = Pos.Y (new() { Frame = testRect });
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
// Pos.Y(win) + 0
pos = Pos.Y (new View { Frame = testRect }) + testInt;
pos = Pos.Y (new() { Frame = testRect }) + testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -864,7 +767,7 @@ public class PosTests
testInt = 1;
// Pos.Y(win) +1
pos = Pos.Y (new View { Frame = testRect }) + testInt;
pos = Pos.Y (new() { Frame = testRect }) + testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -874,7 +777,7 @@ public class PosTests
testInt = -1;
// Pos.Y(win) -1
pos = Pos.Y (new View { Frame = testRect }) - testInt;
pos = Pos.Y (new() { Frame = testRect }) - testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -885,18 +788,18 @@ public class PosTests
side = "bottom";
testRect = Rectangle.Empty;
testInt = 0;
pos = Pos.Bottom (new View ());
pos = Pos.Bottom (new ());
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
pos = Pos.Bottom (new View { Frame = testRect });
pos = Pos.Bottom (new() { Frame = testRect });
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
testRect = new Rectangle (1, 2, 3, 4);
pos = Pos.Bottom (new View { Frame = testRect });
testRect = new (1, 2, 3, 4);
pos = Pos.Bottom (new() { Frame = testRect });
Assert.Equal ($"View(side={side},target=View(){testRect})", pos.ToString ());
// Pos.Bottom(win) + 0
pos = Pos.Bottom (new View { Frame = testRect }) + testInt;
pos = Pos.Bottom (new() { Frame = testRect }) + testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -906,7 +809,7 @@ public class PosTests
testInt = 1;
// Pos.Bottom(win) +1
pos = Pos.Bottom (new View { Frame = testRect }) + testInt;
pos = Pos.Bottom (new() { Frame = testRect }) + testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",
@@ -916,7 +819,7 @@ public class PosTests
testInt = -1;
// Pos.Bottom(win) -1
pos = Pos.Bottom (new View { Frame = testRect }) - testInt;
pos = Pos.Bottom (new() { Frame = testRect }) - testInt;
Assert.Equal (
$"Combine(View(side={side},target=View(){testRect}){(testInt < 0 ? '-' : '+')}Absolute({testInt}))",

View File

@@ -405,7 +405,7 @@ public class SetRelativeLayoutTests
{
var screen = new Size (30, 1);
var view = new View { Text = "abc", AutoSize = true }; // BUGBUG: AutoSize or Width must be set
view.X = Pos.AnchorEnd () - Pos.Function (GetViewWidth);
view.X = Pos.AnchorEnd (0) - Pos.Function (GetViewWidth);
int GetViewWidth () { return view.Frame.Width; }

View File

@@ -164,8 +164,8 @@ public class ButtonTests (ITestOutputHelper output)
var btn = new Button { Y = Pos.Center (), Text = "Say Hello 你", AutoSize = true };
var btnTxt = $"{CM.Glyphs.LeftBracket} {btn.Text} {CM.Glyphs.RightBracket}";
btn.X = Pos.AnchorEnd () - Pos.Function (() => btn.TextFormatter.Text.GetColumns ());
btn.X = Pos.AnchorEnd () - Pos.Function (() => btn.TextFormatter.Text.GetColumns ());
btn.X = Pos.AnchorEnd (0) - Pos.Function (() => btn.TextFormatter.Text.GetColumns ());
btn.X = Pos.AnchorEnd (0) - Pos.Function (() => btn.TextFormatter.Text.GetColumns ());
var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
win.Add (btn);

View File

@@ -77,7 +77,7 @@ public class CheckBoxTests
{
var checkBox = new CheckBox { Y = Pos.Center (), Text = "C_heck this out 你" };
checkBox.X = Pos.AnchorEnd () - Pos.Function (() => checkBox.GetSizeNeededForTextWithoutHotKey ().Width);
checkBox.X = Pos.AnchorEnd (0) - Pos.Function (() => checkBox.GetSizeNeededForTextWithoutHotKey ().Width);
var win = new Window { Width = Dim.Fill (), Height = Dim.Fill (), Title = "Test Demo 你" };
win.Add (checkBox);
@@ -121,7 +121,7 @@ public class CheckBoxTests
{
var checkBox = new CheckBox { Y = Pos.Center (), Text = "Check this out 你" };
checkBox.X = Pos.AnchorEnd () - Pos.Function (() => checkBox.GetSizeNeededForTextWithoutHotKey ().Width);
checkBox.X = Pos.AnchorEnd (0) - Pos.Function (() => checkBox.GetSizeNeededForTextWithoutHotKey ().Width);
var win = new Window { Width = Dim.Fill (), Height = Dim.Fill (), Title = "Test Demo 你" };
win.Add (checkBox);

View File

@@ -92,7 +92,7 @@ public class LabelTests
public void AutoSize_Stays_True_AnchorEnd ()
{
var label = new Label { Y = Pos.Center (), Text = "Say Hello 你", AutoSize = true };
label.X = Pos.AnchorEnd () - Pos.Function (() => label.TextFormatter.Text.GetColumns ());
label.X = Pos.AnchorEnd (0) - Pos.Function (() => label.TextFormatter.Text.GetColumns ());
var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
win.Add (label);

View File

@@ -55,7 +55,7 @@ The [Pos](~/api/Terminal.Gui.Pos.yml) is the type of `View.X` and `View.Y` and s
* Absolute position, by passing an integer - `Pos.Absolute(n)`.
* Percentage of the parent's view size - `Pos.Percent(percent)`.
* Anchored from the end of the dimension - `Pos.AnchorEnd(margin)`.
* Anchored from the end of the dimension - `Pos.AnchorEnd()`.
* Centered, using `Pos.Center()`.
* The `Pos.Left(otherView)`, `Pos.Top(otherView)`, `Pos.Bottom(otherView)`, `Pos.Right(otherView)` positions of another view.

View File

@@ -24,6 +24,7 @@ The entire library has been reviewed and simplified. As a result, the API is mor
* *Adornments* -
* *Built-in Scrolling/Virtual Content Area* - In v1, to have a view a user could scroll required either a bespoke scrolling implementation, inheriting from `ScrollView`, or managing the complexity of `ScrollBarView` directly. In v2, the base-View class supports scrolling inherently. The area of a view visible to the user at a given moment was previously a rectangle called `Bounds`. `Bounds.Location` was always `Point.Empty`. In v2 the visible area is a rectangle called `Viewport` which is a protal into the Views content, which can be bigger (or smaller) than the area visible to the user. Causing a view to scroll is as simple as changing `View.Viewport.Location`. The View's content described by `View.ContentSize`. See [Layout](layout.md) for details.
* *Computed Layout Improvements* -
* *`Pos.AnchorEnd ()`* - New to v2 is `Pos.AnchorEnd ()` (with no parameters) which allows a view to be anchored to the right or bottom of the Superview.
* *`Dim.Auto`* -
* ...