Messing with Pos.Justify

This commit is contained in:
Tig
2024-04-21 16:54:07 -06:00
parent 04f75dcc6f
commit a89ffaf6d0
6 changed files with 137 additions and 113 deletions

View File

@@ -85,17 +85,31 @@ public enum Justification
/// </summary>
public class Justifier
{
private int _maxSpaceBetweenItems;
/// <summary>
/// Gets or sets how the <see cref="Justifier"/> justifies items within a container.
/// </summary>
public Justification Justification { get; set; }
/// <summary>
/// The size of the container.
/// </summary>
public int ContainerSize { get; set; }
/// <summary>
/// Gets or sets whether <see cref="Justify"/> puts a space is placed between items. Default is <see langword="false"/>. If <see langword="true"/>, a space will be
/// placed between each item, which is useful for
/// justifying text.
/// placed between each item, which is useful for justifying text.
/// </summary>
public bool PutSpaceBetweenItems
public bool PutSpaceBetweenItems { get; set; }
/// <summary>
/// Takes a list of items and returns their positions when justified within a container <see name="ContainerSize"/> wide based on the specified
/// <see cref="Justification"/>.
/// </summary>
/// <param name="sizes">The sizes of the items to justify.</param>
/// <returns>The locations of the items, from left to right.</returns>
public int [] Justify (int [] sizes)
{
get => _maxSpaceBetweenItems == 1;
set => _maxSpaceBetweenItems = value ? 1 : 0;
return Justify (Justification, PutSpaceBetweenItems, ContainerSize, sizes);
}
/// <summary>
@@ -104,28 +118,23 @@ public class Justifier
/// </summary>
/// <param name="sizes">The sizes of the items to justify.</param>
/// <param name="justification">The justification style.</param>
/// <param name="containerSize">The width of the container.</param>
/// <param name="containerSize">The size of the container.</param>
/// <returns>The locations of the items, from left to right.</returns>
public int [] Justify (int [] sizes, Justification justification, int containerSize)
public static int [] Justify (Justification justification, bool putSpaceBetweenItems, int containerSize, int [] sizes)
{
if (sizes.Length == 0)
{
return new int [] { };
}
int maxSpaceBetweenItems = putSpaceBetweenItems ? 1 : 0;
var positions = new int [sizes.Length]; // positions of the items. the return value.
int totalItemsSize = sizes.Sum ();
int totalGaps = sizes.Length - 1; // total gaps between items
int totalItemsAndSpaces = totalItemsSize + totalGaps * maxSpaceBetweenItems; // total size of items and spaces if we had enough room
if (totalItemsSize > containerSize)
{
// throw new ArgumentException ("The sum of the sizes is greater than the total size.");
}
var positions = new int [sizes.Length];
totalItemsSize = sizes.Sum (); // total size of items
int totalGaps = sizes.Length - 1; // total gaps (MinimumSpaceBetweenItems)
int totalItemsAndSpaces = totalItemsSize + totalGaps * _maxSpaceBetweenItems; // total size of items and spaces if we had enough room
int spaces = totalGaps * _maxSpaceBetweenItems; // We'll decrement this below to place one space between each item until we run out
int spaces = totalGaps * maxSpaceBetweenItems; // We'll decrement this below to place one space between each item until we run out
if (totalItemsSize >= containerSize)
{
spaces = 0;
@@ -154,7 +163,7 @@ public class Justifier
continue;
}
int spaceBefore = spaces-- > 0 ? _maxSpaceBetweenItems : 0;
int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0;
// subsequent items are placed one space after the previous item
positions [i] = positions [i - 1] + sizes [i - 1] + spaceBefore;
@@ -171,7 +180,7 @@ public class Justifier
throw new ArgumentException ("The size of an item cannot be negative.");
}
int spaceBefore = spaces-- > 0 ? _maxSpaceBetweenItems : 0;
int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0;
positions [i] = currentPosition;
currentPosition += sizes [i] + spaceBefore;
@@ -199,7 +208,7 @@ public class Justifier
continue;
}
int spaceBefore = spaces-- > 0 ? _maxSpaceBetweenItems : 0;
int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0;
// subsequent items are placed one space after the previous item
positions [i] = positions [i - 1] + sizes [i - 1] + spaceBefore;
@@ -251,7 +260,7 @@ public class Justifier
if (i < sizes.Length - 1)
{
int spaceBefore = spaces-- > 0 ? _maxSpaceBetweenItems : 0;
int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0;
positions [i] = currentPosition;
currentPosition += sizes [i] + spaceBefore;
@@ -295,7 +304,7 @@ public class Justifier
if (i < sizes.Length - 1 && i > 0)
{
int spaceBefore = spaces-- > 0 ? _maxSpaceBetweenItems : 0;
int spaceBefore = spaces-- > 0 ? maxSpaceBetweenItems : 0;
positions [i] = currentPosition - sizes [i] - spaceBefore;
currentPosition = positions [i];

View File

@@ -469,7 +469,8 @@ public class Pos
/// </summary>
public class PosJustify : Pos
{
private readonly Justification _justification;
internal readonly Justifier _justifier;
internal int? _location;
/// <summary>
/// Enables justification of a set of views.
@@ -478,81 +479,39 @@ public class Pos
/// <param name="justification"></param>
public PosJustify (Justification justification)
{
_justification = justification;
_justifier = new ()
{
PutSpaceBetweenItems = false,
Justification = justification,
};
}
public override bool Equals (object other)
{
return other is PosJustify justify && justify._justification == _justification;
return other is PosJustify justify && justify._justifier == _justifier;
}
public override int GetHashCode () { return _justification.GetHashCode (); }
public override int GetHashCode () { return _justifier.GetHashCode (); }
public override string ToString ()
{
return $"Justify(alignment={_justification})";
return $"Justify(justification={_justifier.Justification})";
}
internal override int Anchor (int width)
{
return width;
return _location ?? 0 - width;
}
internal override int Calculate (int superviewDimension, Dim dim, View us, Dim.Dimension dimension)
{
if (us.SuperView is null)
if (_location.HasValue)
{
return 0;
}
// Find all the views that are being justified - they have the same justification and opposite position as us
// Then, pass the array of views to the Justify method
int [] dimensions;
int [] positions;
int ourIndex = 0;
if (dimension == Dim.Dimension.Width)
{
List<int> dimensionsList = new List<int> ();
for (int i = 0; i < us.SuperView.Subviews.Count; i++)
{
var v = us.SuperView.Subviews [i];
var j = v.X as PosJustify;
if (j?._justification == _justification && v.Frame.Y == us.Frame.Y)
{
dimensionsList.Add (v.Frame.Width);
if (v == us)
{
ourIndex = dimensionsList.Count - 1;
}
}
}
dimensions = dimensionsList.ToArray ();
positions = new Justifier () { PutSpaceBetweenItems = true }.Justify (dimensions, _justification, superviewDimension);
}
else
{
List<int> dimensionsList = new List<int> ();
for (int i = 0; i < us.SuperView.Subviews.Count; i++)
{
var v = us.SuperView.Subviews [i];
var j = v.Y as PosJustify;
if (j?._justification == _justification && v.Frame.X == us.Frame.X)
{
dimensionsList.Add (v.Frame.Height);
if (v == us)
{
ourIndex = dimensionsList.Count - 1;
}
}
}
dimensions = dimensionsList.ToArray ();
positions = new Justifier () { PutSpaceBetweenItems = false }.Justify (dimensions, _justification, superviewDimension);
return _location.Value;
}
return positions [ourIndex];
return 0;
}
}

View File

@@ -1,4 +1,5 @@
using System.Diagnostics;
using static Terminal.Gui.Pos;
namespace Terminal.Gui;
@@ -850,13 +851,62 @@ public partial class View
foreach (View v in ordered)
{
// TODO: Move this logic into the Pos/Dim classes
var justifyX = v.X as PosJustify;
var justifyY = v.Y as PosJustify;
if (justifyX is { } || justifyY is { })
{
int xIndex = 0;
int yIndex = 0;
List<int> XdimensionsList = new ();
List<int> YdimensionsList = new ();
for (int i = 0; i < v.SuperView.Subviews.Count; i++)
{
var viewI = v.SuperView.Subviews [i];
var jX = viewI.X as PosJustify;
var jY = viewI.Y as PosJustify;
if (jX?._justifier.Justification == justifyX?._justifier.Justification && viewI.Frame.Y == v.Frame.Y)
{
XdimensionsList.Add (viewI.Frame.Width);
if (viewI == v)
{
xIndex = XdimensionsList.Count - 1;
}
}
if (jY?._justifier.Justification == justifyY?._justifier.Justification && viewI.Frame.X == v.Frame.X)
{
YdimensionsList.Add (viewI.Frame.Height);
if (viewI == v)
{
yIndex = YdimensionsList.Count - 1;
}
}
}
if (justifyX is { })
{
justifyX._justifier.ContainerSize = Viewport.Size.Width;
justifyX._location = justifyX._justifier.Justify (XdimensionsList.ToArray ()) [xIndex];
}
if (justifyY is { })
{
justifyY._justifier.ContainerSize = Viewport.Size.Height;
justifyY._location = justifyY._justifier.Justify (YdimensionsList.ToArray ()) [yIndex];
}
}
// TODO: Move this logic into the Pos/Dim classes
if (v.Width is Dim.DimAuto || v.Height is Dim.DimAuto)
{
// If the view is auto-sized...
Rectangle f = v.Frame;
v._frame = new (v.Frame.X, v.Frame.Y, 0, 0);
v._frame = v.Frame with { Width = 0, Height = 0 };
LayoutSubview (v, Viewport.Size);
if (v.Frame != f)
@@ -1005,8 +1055,8 @@ public partial class View
{
//if (AutoSize)
{
// SetFrameToFitText ();
SetTextFormatterSize ();
// SetFrameToFitText ();
SetTextFormatterSize ();
}
LayoutAdornments ();
@@ -1064,15 +1114,6 @@ public partial class View
CheckDimAuto ();
SetTextFormatterSize ();
var autoSize = Size.Empty;
//if (AutoSize)
//{
// // TODO: Nuke this from orbit once Dim.Auto is fully implemented
// autoSize = GetTextAutoSize ();
//}
SetTextFormatterSize ();
if (TextFormatter.NeedsFormat)
{

View File

@@ -45,7 +45,7 @@ public class Button : View
_leftDefault = Glyphs.LeftDefaultIndicator;
_rightDefault = Glyphs.RightDefaultIndicator;
Height = 1;
Height = Dim.Auto (Dim.DimAutoStyle.Text);
Width = Dim.Auto (Dim.DimAutoStyle.Text);
CanFocus = true;

View File

@@ -18,9 +18,10 @@ public sealed class MyScenario : Scenario
};
int leftMargin = 0;
var just = Justification.Justified;
var just = Justification.Centered;
var button = new Button { X = Pos.Justify(just), Y = Pos.Center (), Text = "Press me!" };
//button.Margin.Thickness = new Thickness (leftMargin, 0, 0, 0);
button.Accept += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "Ok");
appWindow.Add (button);

View File

@@ -19,26 +19,30 @@ public class JustifierTests (ITestOutputHelper output)
[MemberData (nameof (JustificationEnumValues))]
public void NoItems_Works (Justification justification)
{
int [] sizes = { };
int [] positions = new Justifier ().Justify (sizes, justification, 100);
int [] sizes = [];
int [] positions = Justifier.Justify (justification, false, 100, sizes);
Assert.Equal (new int [] { }, positions);
}
//[Theory]
//[MemberData (nameof (JustificationEnumValues))]
//public void Items_Width_Cannot_Exceed_TotalSize (Justification justification)
//{
// int [] sizes = { 1000, 2000, 3000 };
// Assert.Throws<ArgumentException> (() => new Justifier ().Justify (sizes, justification, 100));
//}
[Theory]
[MemberData (nameof (JustificationEnumValues))]
public void Negative_Widths_Not_Allowed (Justification justification)
{
Assert.Throws<ArgumentException> (() => new Justifier ().Justify (new [] { -10, 20, 30 }, justification, 100));
Assert.Throws<ArgumentException> (() => new Justifier ().Justify (new [] { 10, -20, 30 }, justification, 100));
Assert.Throws<ArgumentException> (() => new Justifier ().Justify (new [] { 10, 20, -30 }, justification, 100));
Assert.Throws<ArgumentException> (() => new Justifier ()
{
Justification = justification,
ContainerSize = 100
}.Justify (new [] { -10, 20, 30 }));
Assert.Throws<ArgumentException> (() => new Justifier ()
{
Justification = justification,
ContainerSize = 100
}.Justify (new [] { 10, -20, 30 }));
Assert.Throws<ArgumentException> (() => new Justifier ()
{
Justification = justification,
ContainerSize = 100
}.Justify (new [] { 10, 20, -30 }));
}
[Theory]
@@ -197,10 +201,15 @@ public class JustifierTests (ITestOutputHelper output)
[InlineData (Justification.FirstLeftRestRight, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 0, 10, 30, 60, 101 })]
[InlineData (Justification.FirstLeftRestRight, new [] { 3, 3, 3 }, 21, new [] { 0, 14, 18 })]
[InlineData (Justification.FirstLeftRestRight, new [] { 3, 4, 5 }, 21, new [] { 0, 11, 16 })]
public void TestJustifications_PutSpaceBetweenItems (Justification justification, int [] sizes, int totalSize, int [] expected)
public void TestJustifications_PutSpaceBetweenItems (Justification justification, int [] sizes, int containerSize, int [] expected)
{
int [] positions = new Justifier { PutSpaceBetweenItems = true }.Justify (sizes, justification, totalSize);
AssertJustification (justification, sizes, totalSize, positions, expected);
int [] positions = new Justifier
{
PutSpaceBetweenItems = true,
Justification = justification,
ContainerSize = containerSize
}.Justify (sizes);
AssertJustification (justification, sizes, containerSize, positions, expected);
}
[Theory]
@@ -341,10 +350,15 @@ public class JustifierTests (ITestOutputHelper output)
[InlineData (Justification.FirstLeftRestRight, new [] { 10, 20, 30 }, 101, new [] { 0, 51, 71 })]
[InlineData (Justification.FirstLeftRestRight, new [] { 10, 20, 30, 40 }, 101, new [] { 0, 11, 31, 61 })]
[InlineData (Justification.FirstLeftRestRight, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 0, 11, 31, 61, 101 })]
public void TestJustifications_NoSpaceBetweenItems (Justification justification, int [] sizes, int totalSize, int [] expected)
public void TestJustifications_NoSpaceBetweenItems (Justification justification, int [] sizes, int containerSize, int [] expected)
{
int [] positions = new Justifier { PutSpaceBetweenItems = false }.Justify (sizes, justification, totalSize);
AssertJustification (justification, sizes, totalSize, positions, expected);
int [] positions = new Justifier
{
PutSpaceBetweenItems = false,
Justification = justification,
ContainerSize = containerSize
}.Justify (sizes);
AssertJustification (justification, sizes, containerSize, positions, expected);
}
public void AssertJustification (Justification justification, int [] sizes, int totalSize, int [] positions, int [] expected)