mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-30 09:47:58 +01:00
Messing with Pos.Justify
This commit is contained in:
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user