diff --git a/Terminal.Gui/Drawing/Justification.cs b/Terminal.Gui/Drawing/Justification.cs index e9a9bdbf9..16c76c372 100644 --- a/Terminal.Gui/Drawing/Justification.cs +++ b/Terminal.Gui/Drawing/Justification.cs @@ -85,17 +85,31 @@ public enum Justification /// public class Justifier { - private int _maxSpaceBetweenItems; + /// + /// Gets or sets how the justifies items within a container. + /// + public Justification Justification { get; set; } + + /// + /// The size of the container. + /// + public int ContainerSize { get; set; } /// /// Gets or sets whether puts a space is placed between items. Default is . If , a space will be - /// placed between each item, which is useful for - /// justifying text. + /// placed between each item, which is useful for justifying text. /// - public bool PutSpaceBetweenItems + public bool PutSpaceBetweenItems { get; set; } + + /// + /// Takes a list of items and returns their positions when justified within a container wide based on the specified + /// . + /// + /// The sizes of the items to justify. + /// The locations of the items, from left to right. + public int [] Justify (int [] sizes) { - get => _maxSpaceBetweenItems == 1; - set => _maxSpaceBetweenItems = value ? 1 : 0; + return Justify (Justification, PutSpaceBetweenItems, ContainerSize, sizes); } /// @@ -104,28 +118,23 @@ public class Justifier /// /// The sizes of the items to justify. /// The justification style. - /// The width of the container. + /// The size of the container. /// The locations of the items, from left to right. - 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]; diff --git a/Terminal.Gui/View/Layout/PosDim.cs b/Terminal.Gui/View/Layout/PosDim.cs index 5e1b2a58b..37e8580df 100644 --- a/Terminal.Gui/View/Layout/PosDim.cs +++ b/Terminal.Gui/View/Layout/PosDim.cs @@ -469,7 +469,8 @@ public class Pos /// public class PosJustify : Pos { - private readonly Justification _justification; + internal readonly Justifier _justifier; + internal int? _location; /// /// Enables justification of a set of views. @@ -478,81 +479,39 @@ public class Pos /// 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 dimensionsList = new List (); - 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 dimensionsList = new List (); - 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; } } diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs index e112100bc..877d07bfb 100644 --- a/Terminal.Gui/View/Layout/ViewLayout.cs +++ b/Terminal.Gui/View/Layout/ViewLayout.cs @@ -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 XdimensionsList = new (); + List 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) { diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 15a30def0..dc10b4397 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -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; diff --git a/UICatalog/Scenarios/Generic.cs b/UICatalog/Scenarios/Generic.cs index ca5ee4d35..b4da21031 100644 --- a/UICatalog/Scenarios/Generic.cs +++ b/UICatalog/Scenarios/Generic.cs @@ -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); diff --git a/UnitTests/Drawing/JustifierTests.cs b/UnitTests/Drawing/JustifierTests.cs index 3f0a08188..2f98ec430 100644 --- a/UnitTests/Drawing/JustifierTests.cs +++ b/UnitTests/Drawing/JustifierTests.cs @@ -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 (() => new Justifier ().Justify (sizes, justification, 100)); - //} - [Theory] [MemberData (nameof (JustificationEnumValues))] public void Negative_Widths_Not_Allowed (Justification justification) { - Assert.Throws (() => new Justifier ().Justify (new [] { -10, 20, 30 }, justification, 100)); - Assert.Throws (() => new Justifier ().Justify (new [] { 10, -20, 30 }, justification, 100)); - Assert.Throws (() => new Justifier ().Justify (new [] { 10, 20, -30 }, justification, 100)); + Assert.Throws (() => new Justifier () + { + Justification = justification, + ContainerSize = 100 + }.Justify (new [] { -10, 20, 30 })); + Assert.Throws (() => new Justifier () + { + Justification = justification, + ContainerSize = 100 + }.Justify (new [] { 10, -20, 30 })); + Assert.Throws (() => 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)