From 4ed5ed8bb68f54876c0bf4b6840c28709145c416 Mon Sep 17 00:00:00 2001 From: Tig Date: Wed, 17 Apr 2024 12:51:19 -0600 Subject: [PATCH 01/11] Initial commit. Just a prototype --- Terminal.Gui/View/Layout/PosDim.cs | 220 +++++++++++++++------ Terminal.Gui/View/Layout/ViewLayout.cs | 1 + Terminal.Gui/Views/Dialog.cs | 29 +-- Terminal.Gui/Views/TextView.cs | 5 +- Terminal.Gui/Views/Wizard/Wizard.cs | 2 +- UICatalog/Scenarios/ComputedLayout.cs | 25 +-- UICatalog/Scenarios/Dialogs.cs | 2 +- UnitTests/Configuration/ThemeScopeTests.cs | 6 +- UnitTests/Configuration/ThemeTests.cs | 6 +- UnitTests/Dialogs/DialogTests.cs | 86 ++++---- 10 files changed, 242 insertions(+), 140 deletions(-) diff --git a/Terminal.Gui/View/Layout/PosDim.cs b/Terminal.Gui/View/Layout/PosDim.cs index 4438fb844..f94209390 100644 --- a/Terminal.Gui/View/Layout/PosDim.cs +++ b/Terminal.Gui/View/Layout/PosDim.cs @@ -1,4 +1,22 @@ -namespace Terminal.Gui; +using static Terminal.Gui.Dialog; + +namespace Terminal.Gui; + +/// Determines the horizontal alignment of Views. +public enum ViewAlignments +{ + /// Center-aligns the buttons (the default). + Center = 0, + + /// Justifies the buttons + Justify, + + /// Left-aligns the buttons + Left, + + /// Right-aligns the buttons + Right +} /// /// Describes the position of a which can be an absolute value, a percentage, centered, or @@ -169,14 +187,6 @@ public class Pos /// The value to convert to the . public static Pos At (int n) { return new PosAbsolute (n); } - /// - /// Creates a object that tracks the Bottom (Y+Height) coordinate of the specified - /// - /// - /// The that depends on the other view. - /// The that will be tracked. - public static Pos Bottom (View view) { return new PosView (view, Side.Bottom); } - /// Creates a object that can be used to center the . /// The center Pos. /// @@ -193,6 +203,12 @@ public class Pos /// public static Pos Center () { return new PosCenter (); } + public static Pos Justify (View[] views, ViewAlignments alignment) + { + return new PosJustify (views, alignment); + } + + /// Determines whether the specified object is equal to the current object. /// The object to compare with the current object. /// @@ -213,11 +229,6 @@ public class Pos /// A hash code for the current object. public override int GetHashCode () { return Anchor (0).GetHashCode (); } - /// Creates a object that tracks the Left (X) position of the specified . - /// The that depends on the other view. - /// The that will be tracked. - public static Pos Left (View view) { return new PosView (view, Side.X); } - /// Adds a to a , yielding a new . /// The first to add. /// The second to add. @@ -293,6 +304,34 @@ public class Pos return new PosFactor (percent / 100); } + /// Creates a object that tracks the Top (Y) position of the specified . + /// The that depends on the other view. + /// The that will be tracked. + public static Pos Top (View view) { return new PosView (view, Side.Y); } + + /// Creates a object that tracks the Top (Y) position of the specified . + /// The that depends on the other view. + /// The that will be tracked. + public static Pos Y (View view) { return new PosView (view, Side.Y); } + + /// Creates a object that tracks the Left (X) position of the specified . + /// The that depends on the other view. + /// The that will be tracked. + public static Pos Left (View view) { return new PosView (view, Side.X); } + + /// Creates a object that tracks the Left (X) position of the specified . + /// The that depends on the other view. + /// The that will be tracked. + public static Pos X (View view) { return new PosView (view, Side.X); } + + /// + /// Creates a object that tracks the Bottom (Y+Height) coordinate of the specified + /// + /// + /// The that depends on the other view. + /// The that will be tracked. + public static Pos Bottom (View view) { return new PosView (view, Side.Bottom); } + /// /// Creates a object that tracks the Right (X+Width) coordinate of the specified /// . @@ -301,21 +340,6 @@ public class Pos /// The that will be tracked. public static Pos Right (View view) { return new PosView (view, Side.Right); } - /// Creates a object that tracks the Top (Y) position of the specified . - /// The that depends on the other view. - /// The that will be tracked. - public static Pos Top (View view) { return new PosView (view, Side.Y); } - - /// Creates a object that tracks the Left (X) position of the specified . - /// The that depends on the other view. - /// The that will be tracked. - public static Pos X (View view) { return new PosView (view, Side.X); } - - /// Creates a object that tracks the Top (Y) position of the specified . - /// The that depends on the other view. - /// The that will be tracked. - public static Pos Y (View view) { return new PosView (view, Side.Y); } - /// /// 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. @@ -460,7 +484,7 @@ public class Pos internal override int Anchor (int width) { return _function (); } } - internal enum Side + public enum Side { X = 0, Y = 1, @@ -478,13 +502,13 @@ public class Pos public override string ToString () { string sideString = side switch - { - Side.X => "x", - Side.Y => "y", - Side.Right => "right", - Side.Bottom => "bottom", - _ => "unknown" - }; + { + Side.X => "x", + Side.Y => "y", + Side.Right => "right", + Side.Bottom => "bottom", + _ => "unknown" + }; if (Target == null) { @@ -497,15 +521,101 @@ public class Pos internal override int Anchor (int width) { return side switch - { - Side.X => Target.Frame.X, - Side.Y => Target.Frame.Y, - Side.Right => Target.Frame.Right, - Side.Bottom => Target.Frame.Bottom, - _ => 0 - }; + { + Side.X => Target.Frame.X, + Side.Y => Target.Frame.Y, + Side.Right => Target.Frame.Right, + Side.Bottom => Target.Frame.Bottom, + _ => 0 + }; } } + + + /// + /// Enables justification of a set of views. + /// + public class PosJustify : Pos + { + private readonly View [] _views; + private readonly ViewAlignments _alignment; + + /// + /// Enables justification of a set of views. + /// + /// The set of views to justify according to . + /// + public PosJustify (View [] views, ViewAlignments alignment) + { + _alignment = alignment; + _views = views; + } + + public override bool Equals (object other) + { + return other is PosJustify justify && justify._views == _views && justify._alignment == _alignment; + } + + public override int GetHashCode () { return _views.GetHashCode (); } + + + public override string ToString () + { + return $"Justify(views={_views},alignment={_alignment})"; + } + + internal override int Anchor (int width) + { + if (_views.Length == 0 || !_views [0].IsInitialized) + { + return 0; + } + int spacing = 0; + switch (_alignment) + { + case ViewAlignments.Center: + // Center spacing is sum of the widths of the views - width / number of views + spacing = (width - _views.Select (v => v.Frame.Width).Sum ()) / _views.Length; + + // How do I know which view we are? + View us = _views.Where (v => v.X.Equals (this)).First(); + + if (_views [0] == us) + { + return spacing; + } + // Calculate the position of the previous (left or above us) view + int previous = _views.Where (v => v.X.Equals (us)).First().Frame.Left; + + return previous + spacing; + //case ViewAlignments.Left: + // return Left (width); + //case ViewAlignments.Right: + // return Right (width); + //case ViewAlignments.Justify: + // return Justify (width); + default: + return 0; + + } + } + + //internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize) + //{ + // // Assuming autosize is the size that the View would have if it were to automatically adjust its size based on its content + // // and autoSize is a boolean value that indicates whether the View should automatically adjust its size based on its content + // if (autoSize) + // { + // return autosize; + // } + // else + // { + // // Assuming dim.Calculate returns the calculated size of the View + // return dim.Calculate (_views.Frame.Left, _views.Frame.Width, autosize, autoSize); + // } + //} + + } } /// @@ -822,7 +932,7 @@ public class Dim internal override int Anchor (int width) { return _function (); } } - internal enum Side + public enum Side { Height = 0, Width = 1 @@ -850,11 +960,11 @@ public class Dim } string sideString = _side switch - { - Side.Height => "Height", - Side.Width => "Width", - _ => "unknown" - }; + { + Side.Height => "Height", + Side.Width => "Width", + _ => "unknown" + }; return $"View({sideString},{Target})"; } @@ -862,11 +972,11 @@ public class Dim internal override int Anchor (int width) { return _side switch - { - Side.Height => Target.Frame.Height, - Side.Width => Target.Frame.Width, - _ => 0 - }; + { + Side.Height => Target.Frame.Height, + Side.Width => Target.Frame.Width, + _ => 0 + }; } } } diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs index 1afa0994b..5c6f0cc64 100644 --- a/Terminal.Gui/View/Layout/ViewLayout.cs +++ b/Terminal.Gui/View/Layout/ViewLayout.cs @@ -33,6 +33,7 @@ public enum LayoutStyle Computed } + public partial class View { #region Frame diff --git a/Terminal.Gui/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs index 46787265e..cd859ac0b 100644 --- a/Terminal.Gui/Views/Dialog.cs +++ b/Terminal.Gui/Views/Dialog.cs @@ -15,21 +15,6 @@ namespace Terminal.Gui; /// public class Dialog : Window { - /// Determines the horizontal alignment of the Dialog buttons. - public enum ButtonAlignments - { - /// Center-aligns the buttons (the default). - Center = 0, - - /// Justifies the buttons - Justify, - - /// Left-aligns the buttons - Left, - - /// Right-aligns the buttons - Right - } // TODO: Reenable once border/borderframe design is settled /// @@ -109,7 +94,7 @@ public class Dialog : Window } /// Determines how the s are aligned along the bottom of the dialog. - public ButtonAlignments ButtonAlignment { get; set; } + public ViewAlignments ButtonAlignment { get; set; } /// Optional buttons to lay out at the bottom of the dialog. public Button [] Buttons @@ -129,11 +114,11 @@ public class Dialog : Window } } - /// The default for . + /// The default for . /// This property can be set in a Theme. [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] [JsonConverter (typeof (JsonStringEnumConverter))] - public static ButtonAlignments DefaultButtonAlignment { get; set; } = ButtonAlignments.Center; + public static ViewAlignments DefaultButtonAlignment { get; set; } = ViewAlignments.Center; /// /// Adds a to the , its layout will be controlled by the @@ -200,7 +185,7 @@ public class Dialog : Window switch (ButtonAlignment) { - case ButtonAlignments.Center: + case ViewAlignments.Center: // Center Buttons shiftLeft = (Viewport.Width - buttonsWidth - _buttons.Count - 1) / 2 + 1; @@ -223,7 +208,7 @@ public class Dialog : Window break; - case ButtonAlignments.Justify: + case ViewAlignments.Justify: // Justify Buttons // leftmost and rightmost buttons are hard against edges. The rest are evenly spaced. @@ -258,7 +243,7 @@ public class Dialog : Window break; - case ButtonAlignments.Left: + case ViewAlignments.Left: // Left Align Buttons Button prevButton = _buttons [0]; prevButton.X = 0; @@ -274,7 +259,7 @@ public class Dialog : Window break; - case ButtonAlignments.Right: + case ViewAlignments.Right: // Right align buttons shiftLeft = _buttons [_buttons.Count - 1].Frame.Width; _buttons [_buttons.Count - 1].X = Pos.AnchorEnd (shiftLeft); diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index a20b4cbbc..12cdc5961 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -4180,7 +4180,10 @@ public class TextView : View } else { - PositionCursor (); + if (IsInitialized) + { + PositionCursor (); + } } OnUnwrappedCursorPosition (); diff --git a/Terminal.Gui/Views/Wizard/Wizard.cs b/Terminal.Gui/Views/Wizard/Wizard.cs index c3691b3e1..eddef7f4c 100644 --- a/Terminal.Gui/Views/Wizard/Wizard.cs +++ b/Terminal.Gui/Views/Wizard/Wizard.cs @@ -85,7 +85,7 @@ public class Wizard : Dialog { // Using Justify causes the Back and Next buttons to be hard justified against // the left and right edge - ButtonAlignment = ButtonAlignments.Justify; + ButtonAlignment = ViewAlignments.Justify; BorderStyle = LineStyle.Double; //// Add a horiz separator diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs index c8013c46a..fea26eab9 100644 --- a/UICatalog/Scenarios/ComputedLayout.cs +++ b/UICatalog/Scenarios/ComputedLayout.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Terminal.Gui; +using static Terminal.Gui.Dialog; namespace UICatalog.Scenarios; @@ -332,13 +333,13 @@ public class ComputedLayout : Scenario // This is intentionally convoluted to illustrate potential bugs. var anchorEndLabel1 = new Label { - Text = "This Label should be the 2nd to last line (AnchorEnd (2)).", + Text = "This Label should be the 3rd to last line (AnchorEnd (3)).", TextAlignment = TextAlignment.Centered, ColorScheme = Colors.ColorSchemes ["Menu"], AutoSize = false, Width = Dim.Fill (5), X = 5, - Y = Pos.AnchorEnd (2) + Y = Pos.AnchorEnd (3) }; app.Add (anchorEndLabel1); @@ -347,20 +348,19 @@ public class ComputedLayout : Scenario var anchorEndLabel2 = new TextField { Text = - "This TextField should be the 3rd to last line (AnchorEnd (2) - 1).", + "This TextField should be the 4th to last line (AnchorEnd (3) - 1).", TextAlignment = TextAlignment.Left, ColorScheme = Colors.ColorSchemes ["Menu"], AutoSize = false, Width = Dim.Fill (5), X = 5, - Y = Pos.AnchorEnd (2) - 1 // Pos.Combine + Y = Pos.AnchorEnd (3) - 1 // Pos.Combine }; app.Add (anchorEndLabel2); - // Show positioning vertically using Pos.AnchorEnd via Pos.Combine var leftButton = new Button { - Text = "Left", Y = Pos.AnchorEnd (0) - 1 // Pos.Combine + Text = "Left", Y = Pos.AnchorEnd () - 1 }; leftButton.Accept += (s, e) => @@ -376,7 +376,7 @@ public class ComputedLayout : Scenario // show positioning vertically using Pos.AnchorEnd var centerButton = new Button { - Text = "Center", X = Pos.Center (), Y = Pos.AnchorEnd (1) // Pos.AnchorEnd(1) + Text = "Center", Y = Pos.AnchorEnd (2) // Pos.AnchorEnd(1) }; centerButton.Accept += (s, e) => @@ -402,14 +402,17 @@ public class ComputedLayout : Scenario 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; - + View [] buttons = { leftButton, centerButton, rightButton }; app.Add (leftButton); app.Add (centerButton); app.Add (rightButton); + + // Center three buttons with 5 spaces between them + leftButton.X = Pos.Justify (buttons, ViewAlignments.Center); + centerButton.X = Pos.Justify (buttons, ViewAlignments.Center); + rightButton.X = Pos.Justify (buttons, ViewAlignments.Center); + Application.Run (app); app.Dispose (); } diff --git a/UICatalog/Scenarios/Dialogs.cs b/UICatalog/Scenarios/Dialogs.cs index fa8cacd21..cc2399317 100644 --- a/UICatalog/Scenarios/Dialogs.cs +++ b/UICatalog/Scenarios/Dialogs.cs @@ -265,7 +265,7 @@ public class Dialogs : Scenario dialog = new Dialog { Title = titleEdit.Text, - ButtonAlignment = (Dialog.ButtonAlignments)styleRadioGroup.SelectedItem, + ButtonAlignment = (ViewAlignments)styleRadioGroup.SelectedItem, Buttons = buttons.ToArray () }; diff --git a/UnitTests/Configuration/ThemeScopeTests.cs b/UnitTests/Configuration/ThemeScopeTests.cs index 3da7f881a..b22ae60ee 100644 --- a/UnitTests/Configuration/ThemeScopeTests.cs +++ b/UnitTests/Configuration/ThemeScopeTests.cs @@ -29,12 +29,12 @@ public class ThemeScopeTests { Reset (); Assert.NotEmpty (Themes); - Assert.Equal (Dialog.ButtonAlignments.Center, Dialog.DefaultButtonAlignment); + Assert.Equal (ViewAlignments.Center, Dialog.DefaultButtonAlignment); - Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = Dialog.ButtonAlignments.Right; + Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = ViewAlignments.Right; ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply (); - Assert.Equal (Dialog.ButtonAlignments.Right, Dialog.DefaultButtonAlignment); + Assert.Equal (ViewAlignments.Right, Dialog.DefaultButtonAlignment); Reset (); } diff --git a/UnitTests/Configuration/ThemeTests.cs b/UnitTests/Configuration/ThemeTests.cs index f7d8c732f..c9c2ba2bb 100644 --- a/UnitTests/Configuration/ThemeTests.cs +++ b/UnitTests/Configuration/ThemeTests.cs @@ -77,15 +77,15 @@ public class ThemeTests public void TestSerialize_RoundTrip () { var theme = new ThemeScope (); - theme ["Dialog.DefaultButtonAlignment"].PropertyValue = Dialog.ButtonAlignments.Right; + theme ["Dialog.DefaultButtonAlignment"].PropertyValue = ViewAlignments.Right; string json = JsonSerializer.Serialize (theme, _jsonOptions); var deserialized = JsonSerializer.Deserialize (json, _jsonOptions); Assert.Equal ( - Dialog.ButtonAlignments.Right, - (Dialog.ButtonAlignments)deserialized ["Dialog.DefaultButtonAlignment"].PropertyValue + ViewAlignments.Right, + (ViewAlignments)deserialized ["Dialog.DefaultButtonAlignment"].PropertyValue ); Reset (); } diff --git a/UnitTests/Dialogs/DialogTests.cs b/UnitTests/Dialogs/DialogTests.cs index 6c7eb754a..f74ea5b12 100644 --- a/UnitTests/Dialogs/DialogTests.cs +++ b/UnitTests/Dialogs/DialogTests.cs @@ -32,7 +32,7 @@ public class DialogTests Title = title, Width = width, Height = 1, - ButtonAlignment = Dialog.ButtonAlignments.Center, + ButtonAlignment = ViewAlignments.Center, Buttons = [new Button { Text = btn1Text }] }; @@ -57,7 +57,7 @@ public class DialogTests Title = title, Width = width, Height = 1, - ButtonAlignment = Dialog.ButtonAlignments.Justify, + ButtonAlignment = ViewAlignments.Justify, Buttons = [new Button { Text = btn1Text }] }; @@ -82,7 +82,7 @@ public class DialogTests Title = title, Width = width, Height = 1, - ButtonAlignment = Dialog.ButtonAlignments.Right, + ButtonAlignment = ViewAlignments.Right, Buttons = [new Button { Text = btn1Text }] }; @@ -107,7 +107,7 @@ public class DialogTests Title = title, Width = width, Height = 1, - ButtonAlignment = Dialog.ButtonAlignments.Left, + ButtonAlignment = ViewAlignments.Left, Buttons = [new Button { Text = btn1Text }] }; @@ -155,7 +155,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Center, + ViewAlignments.Center, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -172,7 +172,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Justify, + ViewAlignments.Justify, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -189,7 +189,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Right, + ViewAlignments.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -206,7 +206,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Left, + ViewAlignments.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -248,7 +248,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Center, + ViewAlignments.Center, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -280,7 +280,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Justify, + ViewAlignments.Justify, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -296,7 +296,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Right, + ViewAlignments.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -312,7 +312,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Left, + ViewAlignments.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -353,7 +353,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Center, + ViewAlignments.Center, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -370,7 +370,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Justify, + ViewAlignments.Justify, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -387,7 +387,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Right, + ViewAlignments.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -404,7 +404,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Left, + ViewAlignments.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -447,7 +447,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Center, + ViewAlignments.Center, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -464,7 +464,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Justify, + ViewAlignments.Justify, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -481,7 +481,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Right, + ViewAlignments.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -498,7 +498,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Left, + ViewAlignments.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -530,7 +530,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Center, + ViewAlignments.Center, new Button { Text = btnText } ); @@ -547,7 +547,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Justify, + ViewAlignments.Justify, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -562,7 +562,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Right, + ViewAlignments.Right, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -577,7 +577,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Left, + ViewAlignments.Left, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -594,7 +594,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Center, + ViewAlignments.Center, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -609,7 +609,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Justify, + ViewAlignments.Justify, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -624,7 +624,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Right, + ViewAlignments.Right, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -639,7 +639,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Left, + ViewAlignments.Left, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -673,7 +673,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Center, + ViewAlignments.Center, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text } @@ -689,7 +689,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Justify, + ViewAlignments.Justify, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text } @@ -705,7 +705,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Right, + ViewAlignments.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text } @@ -721,7 +721,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Left, + ViewAlignments.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text } @@ -755,7 +755,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Center, + ViewAlignments.Center, new Button { Text = btn1Text }, new Button { Text = btn2Text } ); @@ -770,7 +770,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Justify, + ViewAlignments.Justify, new Button { Text = btn1Text }, new Button { Text = btn2Text } ); @@ -785,7 +785,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Right, + ViewAlignments.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text } ); @@ -800,7 +800,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Left, + ViewAlignments.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text } ); @@ -837,7 +837,7 @@ public class DialogTests // Default (Center) button1 = new Button { Text = btn1Text }; button2 = new Button { Text = btn2Text }; - (runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Center, button1, button2); + (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Center, button1, button2); button1.Visible = false; RunIteration (ref runstate, ref firstIteration); buttonRow = $@"{CM.Glyphs.VLine} {btn2} {CM.Glyphs.VLine}"; @@ -849,7 +849,7 @@ public class DialogTests Assert.Equal (width, buttonRow.Length); button1 = new Button { Text = btn1Text }; button2 = new Button { Text = btn2Text }; - (runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Justify, button1, button2); + (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Justify, button1, button2); button1.Visible = false; RunIteration (ref runstate, ref firstIteration); buttonRow = $@"{CM.Glyphs.VLine} {btn2}{CM.Glyphs.VLine}"; @@ -861,7 +861,7 @@ public class DialogTests Assert.Equal (width, buttonRow.Length); button1 = new Button { Text = btn1Text }; button2 = new Button { Text = btn2Text }; - (runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Right, button1, button2); + (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Right, button1, button2); button1.Visible = false; RunIteration (ref runstate, ref firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -872,7 +872,7 @@ public class DialogTests Assert.Equal (width, buttonRow.Length); button1 = new Button { Text = btn1Text }; button2 = new Button { Text = btn2Text }; - (runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Left, button1, button2); + (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Left, button1, button2); button1.Visible = false; RunIteration (ref runstate, ref firstIteration); buttonRow = $@"{CM.Glyphs.VLine} {btn2} {CM.Glyphs.VLine}"; @@ -1301,7 +1301,7 @@ public class DialogTests (runstate, Dialog _) = RunButtonTestDialog ( title, width, - Dialog.ButtonAlignments.Center, + ViewAlignments.Center, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -1347,7 +1347,7 @@ public class DialogTests int width = buttonRow.Length; d.SetBufferSize (buttonRow.Length, 3); - (runstate, Dialog _) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Center, null); + (runstate, Dialog _) = RunButtonTestDialog (title, width, ViewAlignments.Center, null); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); End (runstate); @@ -1356,7 +1356,7 @@ public class DialogTests private (RunState, Dialog) RunButtonTestDialog ( string title, int width, - Dialog.ButtonAlignments align, + ViewAlignments align, params Button [] btns ) { From f2a16dc1a3f68e33914da8eef073ee239c720d9e Mon Sep 17 00:00:00 2001 From: Tig Date: Fri, 19 Apr 2024 11:39:16 -0600 Subject: [PATCH 02/11] Added Justifier class with robust unit tests --- Terminal.Gui/Drawing/Justification.cs | 170 ++++++++++++++++++++ Terminal.sln.DotSettings | 1 + UnitTests/Drawing/JustifierTests.cs | 220 ++++++++++++++++++++++++++ 3 files changed, 391 insertions(+) create mode 100644 Terminal.Gui/Drawing/Justification.cs create mode 100644 UnitTests/Drawing/JustifierTests.cs diff --git a/Terminal.Gui/Drawing/Justification.cs b/Terminal.Gui/Drawing/Justification.cs new file mode 100644 index 000000000..88d63fb29 --- /dev/null +++ b/Terminal.Gui/Drawing/Justification.cs @@ -0,0 +1,170 @@ +namespace Terminal.Gui; + +/// +/// Controls how items are justified within a container. Used by . +/// +public enum Justification +{ + /// + /// The items will be left-justified. + /// + Left, + + /// + /// The items will be right-justified. + /// + Right, + + /// + /// The items will be centered. + /// + Centered, + + /// + /// The items will be justified. Space will be added between the items such that the first item + /// is at the start and the right side of the last item against the end. + /// + Justified, + + RightJustified, + LeftJustified +} + +/// +/// Justifies items within a container based on the specified . +/// +public class Justifier +{ + /// + /// Justifies the within a container wide based on the specified + /// . + /// + /// + /// + /// + /// + public static int [] Justify (int [] sizes, Justification justification, int totalSize) + { + var positions = new int [sizes.Length]; + int totalItemsSize = sizes.Sum (); + + if (totalItemsSize > totalSize) + { + throw new ArgumentException ("The sum of the sizes is greater than the total size."); + } + + switch (justification) + { + case Justification.Left: + var currentPosition = 0; + + for (var i = 0; i < sizes.Length; i++) + { + if (sizes [i] < 0) + { + throw new ArgumentException ("The size of an item cannot be negative."); + } + + positions [i] = currentPosition; + currentPosition += sizes [i]; + } + + break; + case Justification.Right: + currentPosition = totalSize - totalItemsSize; + + for (var i = 0; i < sizes.Length; i++) + { + if (sizes [i] < 0) + { + throw new ArgumentException ("The size of an item cannot be negative."); + } + + positions [i] = currentPosition; + currentPosition += sizes [i]; + } + + break; + case Justification.Centered: + currentPosition = (totalSize - totalItemsSize) / 2; + + for (var i = 0; i < sizes.Length; i++) + { + if (sizes [i] < 0) + { + throw new ArgumentException ("The size of an item cannot be negative."); + } + + positions [i] = currentPosition; + currentPosition += sizes [i]; + } + + break; + + case Justification.Justified: + int spaceBetween = sizes.Length > 1 ? (totalSize - totalItemsSize) / (sizes.Length - 1) : 0; + int remainder = sizes.Length > 1 ? (totalSize - totalItemsSize) % (sizes.Length - 1) : 0; + currentPosition = 0; + for (var i = 0; i < sizes.Length; i++) + { + if (sizes [i] < 0) + { + throw new ArgumentException ("The size of an item cannot be negative."); + } + positions [i] = currentPosition; + int extraSpace = i < remainder ? 1 : 0; + currentPosition += sizes [i] + spaceBetween + extraSpace; + } + break; + + case Justification.LeftJustified: + if (sizes.Length > 1) + { + int spaceBetweenLeft = totalSize - sizes.Sum () + 1; // +1 for the extra space + currentPosition = 0; + for (var i = 0; i < sizes.Length - 1; i++) + { + if (sizes [i] < 0) + { + throw new ArgumentException ("The size of an item cannot be negative."); + } + positions [i] = currentPosition; + currentPosition += sizes [i] + 1; // +1 for the extra space + } + positions [sizes.Length - 1] = totalSize - sizes [sizes.Length - 1]; + } + else if (sizes.Length == 1) + { + positions [0] = 0; + } + break; + + case Justification.RightJustified: + if (sizes.Length > 1) + { + totalItemsSize = sizes.Sum (); + int totalSpaces = totalSize - totalItemsSize; + int bigSpace = totalSpaces - (sizes.Length - 2); + + positions [0] = 0; // first item is flush left + positions [1] = sizes [0] + bigSpace; // second item has the big space before it + + // remaining items have one space between them + for (var i = 2; i < sizes.Length; i++) + { + positions [i] = positions [i - 1] + sizes [i - 1] + 1; + } + } + else if (sizes.Length == 1) + { + positions [0] = 0; // single item is flush left + } + break; + + + + } + + return positions; + } +} diff --git a/Terminal.sln.DotSettings b/Terminal.sln.DotSettings index 3bc1fab5d..cdfa4823d 100644 --- a/Terminal.sln.DotSettings +++ b/Terminal.sln.DotSettings @@ -438,5 +438,6 @@ Concurrency Issue (?<=\W|^)(?<TAG>CONCURRENCY:)(\W|$)(.*) Warning + True True diff --git a/UnitTests/Drawing/JustifierTests.cs b/UnitTests/Drawing/JustifierTests.cs new file mode 100644 index 000000000..62f036aa0 --- /dev/null +++ b/UnitTests/Drawing/JustifierTests.cs @@ -0,0 +1,220 @@ + +using System.Text; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Terminal.Gui.DrawingTests; + +public class JustifierTests (ITestOutputHelper output) +{ + + private readonly ITestOutputHelper _output = output; + + [Fact] + public void TestLeftJustification () + { + int [] sizes = { 10, 20, 30 }; + var positions = Justifier.Justify (sizes, Justification.Left, 100); + Assert.Equal (new List { 0, 10, 30 }, positions); + } + + [Fact] + public void TestRightJustification () + { + int [] sizes = { 10, 20, 30 }; + var positions = Justifier.Justify (sizes, Justification.Right, 100); + Assert.Equal (new List { 40, 50, 70 }, positions); + } + + [Fact] + public void TestCenterJustification () + { + int [] sizes = { 10, 20, 30 }; + var positions = Justifier.Justify (sizes, Justification.Centered, 100); + Assert.Equal (new List { 20, 30, 50 }, positions); + } + + [Fact] + public void TestJustifiedJustification () + { + int [] sizes = { 10, 20, 30 }; + var positions = Justifier.Justify (sizes, Justification.Justified, 100); + Assert.Equal (new List { 0, 30, 70 }, positions); + } + + [Fact] + public void TestNoItems () + { + int [] sizes = { }; + var positions = Justifier.Justify (sizes, Justification.Left, 100); + Assert.Equal (new int [] { }, positions); + } + + [Fact] + public void TestTenItems () + { + int [] sizes = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }; + var positions = Justifier.Justify (sizes, Justification.Left, 100); + Assert.Equal (new int [] { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 }, positions); + } + + [Fact] + public void TestZeroLengthItems () + { + int [] sizes = { 0, 0, 0 }; + var positions = Justifier.Justify (sizes, Justification.Left, 100); + Assert.Equal (new int [] { 0, 0, 0 }, positions); + } + + [Fact] + public void TestLongItems () + { + int [] sizes = { 1000, 2000, 3000 }; + Assert.Throws (() => Justifier.Justify (sizes, Justification.Left, 100)); + } + + [Fact] + public void TestNegativeLengths () + { + int [] sizes = { -10, -20, -30 }; + Assert.Throws (() => Justifier.Justify (sizes, Justification.Left, 100)); + } + + [Theory] + [InlineData (Justification.Left, new int [] { 10, 20, 30 }, 100, new int [] { 0, 10, 30 })] + [InlineData (Justification.Left, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 66 })] + [InlineData (Justification.Left, new int [] { 10 }, 101, new int [] { 0 })] + [InlineData (Justification.Left, new int [] { 10, 20 }, 101, new int [] { 0, 10 })] + [InlineData (Justification.Left, new int [] { 10, 20, 30 }, 101, new int [] { 0, 10, 30 })] + [InlineData (Justification.Left, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 60 })] + [InlineData (Justification.Left, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 100 })] + + [InlineData (Justification.Right, new int [] { 10, 20, 30 }, 100, new int [] { 40, 50, 70 })] + [InlineData (Justification.Right, new int [] { 33, 33, 33 }, 100, new int [] { 1, 34, 67 })] + [InlineData (Justification.Right, new int [] { 10 }, 101, new int [] { 91 })] + [InlineData (Justification.Right, new int [] { 10, 20 }, 101, new int [] { 71, 81 })] + [InlineData (Justification.Right, new int [] { 10, 20, 30 }, 101, new int [] { 41, 51, 71 })] + [InlineData (Justification.Right, new int [] { 10, 20, 30, 40 }, 101, new int [] { 1, 11, 31, 61 })] + [InlineData (Justification.Right, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 1, 11, 31, 61, 101 })] + + [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 100, new int [] { 20, 30, 50 })] + [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 99, new int [] { 0, 33, 66 })] + [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 66 })] + [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 101, new int [] { 1, 34, 67 })] + [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 102, new int [] { 1, 34, 67 })] + [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 104, new int [] { 2, 35, 68 })] + [InlineData (Justification.Centered, new int [] { 10 }, 101, new int [] { 45 })] + [InlineData (Justification.Centered, new int [] { 10, 20 }, 101, new int [] { 35, 45 })] + [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 101, new int [] { 20, 30, 50 })] + [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 60 })] + [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 100 })] + + [InlineData (Justification.Justified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] + [InlineData (Justification.Justified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] + [InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 30, 70 })] + [InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 31, 71 })] + [InlineData (Justification.Justified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] + [InlineData (Justification.Justified, new int [] { 11, 17, 23 }, 100, new int [] { 0, 36, 77 })] + [InlineData (Justification.Justified, new int [] { 1, 2, 3 }, 11, new int [] { 0, 4, 8 })] + [InlineData (Justification.Justified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + [InlineData (Justification.Justified, new int [] { 10 }, 101, new int [] { 0 })] + [InlineData (Justification.Justified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 9, 18 })] + [InlineData (Justification.Justified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 8, 16 })] + [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 18, new int [] { 0, 3, 7, 12 })] + [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 19, new int [] { 0, 4, 8, 13 })] + [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 20, new int [] { 0, 4, 9, 14 })] + [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 21, new int [] { 0, 4, 9, 15 })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 22, new int [] { 0, 8, 14, 19 })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 23, new int [] { 0, 8, 15, 20, })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 24, new int [] { 0, 8, 15, 21 })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 25, new int [] { 0, 9, 16, 22 })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 26, new int [] { 0, 9, 17, 23 })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 31, new int [] { 0, 11, 20, 28 })] + + [InlineData (Justification.LeftJustified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 11, 70 })] + [InlineData (Justification.LeftJustified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] + [InlineData (Justification.LeftJustified, new int [] { 10 }, 101, new int [] { 0 })] + [InlineData (Justification.LeftJustified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + [InlineData (Justification.LeftJustified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 11, 71 })] + [InlineData (Justification.LeftJustified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 32, 61 })] + [InlineData (Justification.LeftJustified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 32, 63, 101 })] + [InlineData (Justification.LeftJustified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 4, 18 })] + [InlineData (Justification.LeftJustified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 4, 16 })] + + [InlineData (Justification.RightJustified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 49, 70 })] + [InlineData (Justification.RightJustified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 67 })] + [InlineData (Justification.RightJustified, new int [] { 10 }, 101, new int [] { 0 })] + [InlineData (Justification.RightJustified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + [InlineData (Justification.RightJustified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 50, 71 })] + [InlineData (Justification.RightJustified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 9, 30, 61 })] + [InlineData (Justification.RightJustified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 8, 29, 60, 101 })] + [InlineData (Justification.RightJustified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 14, 18 })] + [InlineData (Justification.RightJustified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 11, 16 })] + + public void TestJustifications (Justification justification, int [] sizes, int totalSize, int [] expected) + { + var positions = Justifier.Justify (sizes, justification, totalSize); + AssertJustification (justification, sizes, totalSize, positions, expected); + } + + public void AssertJustification (Justification justification, int [] sizes, int totalSize, int [] positions, int [] expected) + { + try + { + _output.WriteLine ($"Testing: {RenderJustification (justification, sizes, totalSize, expected)}"); + } + catch (Exception e) + { + _output.WriteLine ($"Exception rendering expected: {e.Message}"); + _output.WriteLine ($"Actual: {RenderJustification (justification, sizes, totalSize, positions)}"); + } + + if (!expected.SequenceEqual(positions)) + { + _output.WriteLine ($"Expected: {RenderJustification (justification, sizes, totalSize, expected)}"); + _output.WriteLine ($"Actual: {RenderJustification (justification, sizes, totalSize, positions)}"); + Assert.Fail(" Expected and actual do not match"); + } + } + + + public string RenderJustification (Justification justification, int [] sizes, int totalSize, int [] positions) + { + var output = new StringBuilder (); + output.AppendLine ($"Justification: {justification}, Positions: {string.Join (", ", positions)}, TotalSize: {totalSize}"); + for (int i = 0; i <= totalSize / 10; i++) + { + output.Append (i.ToString ().PadRight (9) + " "); + } + output.AppendLine (); + + for (int i = 0; i < totalSize; i++) + { + output.Append (i % 10); + } + output.AppendLine (); + + var items = new char [totalSize]; + for (int position = 0; position < positions.Length; position++) + { + try + { + for (int j = 0; j < sizes [position]; j++) + { + items [positions [position] + j] = (position + 1).ToString () [0]; + } + } catch(Exception e) + { + output.AppendLine ($"{e.Message} - position = {position}, positions[{position}]: {positions[position]}, sizes[{position}]: {sizes[position]}, totalSize: {totalSize}"); + output.Append (new string (items).Replace ('\0', ' ')); + + Assert.Fail(e.Message + output.ToString ()); + } + } + + output.Append (new string (items).Replace ('\0', ' ')); + + return output.ToString (); + } + +} From 48cf0db2917112bb3a39209f776f7d10adff8e29 Mon Sep 17 00:00:00 2001 From: Tig Date: Fri, 19 Apr 2024 12:57:07 -0600 Subject: [PATCH 03/11] Working on Centered --- Terminal.Gui/Drawing/Justification.cs | 73 +++++++++++++++-- Terminal.Gui/View/Layout/PosDim.cs | 38 ++++----- UnitTests/Drawing/JustifierTests.cs | 114 +++++++++++++------------- 3 files changed, 142 insertions(+), 83 deletions(-) diff --git a/Terminal.Gui/Drawing/Justification.cs b/Terminal.Gui/Drawing/Justification.cs index 88d63fb29..557d35855 100644 --- a/Terminal.Gui/Drawing/Justification.cs +++ b/Terminal.Gui/Drawing/Justification.cs @@ -16,7 +16,7 @@ public enum Justification Right, /// - /// The items will be centered. + /// The items will be arranged such that there is no more than 1 space between them. The group will be centered in the container. /// Centered, @@ -24,9 +24,33 @@ public enum Justification /// The items will be justified. Space will be added between the items such that the first item /// is at the start and the right side of the last item against the end. /// + /// + /// + /// 111 2222 33333 + /// + /// Justified, + /// + /// The items will be left-justified. The first item will be at the start and the last item will be at the end. + /// Those in between will be tight against the right item. + /// + /// + /// + /// 111 2222 33333 + /// + /// RightJustified, + + /// + /// The items will be left-justified. The first item will be at the start and the last item will be at the end. + /// Those in between will be tight against the right item. + /// + /// + /// + /// 111 2222 33333 + /// + /// LeftJustified } @@ -85,22 +109,53 @@ public class Justifier } break; - case Justification.Centered: - currentPosition = (totalSize - totalItemsSize) / 2; - for (var i = 0; i < sizes.Length; i++) + case Justification.Centered: + if (sizes.Length > 1) { - if (sizes [i] < 0) + totalItemsSize = sizes.Sum (); // total size of items + int totalGaps = sizes.Length - 1; // total gaps (0 or 1 space) + int totalItemsAndSpaces = totalItemsSize + totalGaps; // total size of items and spaces + + int spaces = totalGaps; + + if (totalItemsSize >= totalSize) { - throw new ArgumentException ("The size of an item cannot be negative."); + spaces = 0; + } + else if (totalItemsAndSpaces > totalSize) + { + spaces = totalItemsAndSpaces - totalSize; } - positions [i] = currentPosition; - currentPosition += sizes [i]; - } + int remainingSpace = Math.Max(0, totalSize - totalItemsSize - spaces); // remaining space to be distributed before and after the items + int spaceBefore = remainingSpace / 2; // space before the items + positions [0] = spaceBefore; // first item position + for (var i = 1; i < sizes.Length; i++) + { + int aSpace = 0; + if (spaces > 0) + { + spaces--; + aSpace = 1; + } + // subsequent items are placed one space after the previous item + positions [i] = positions [i - 1] + sizes [i - 1] + aSpace; + } + // Adjust the last position if there is an extra space + if (positions [sizes.Length - 1] + sizes [sizes.Length - 1] > totalSize) + { + positions [sizes.Length - 1]--; + } + } + else if (sizes.Length == 1) + { + positions [0] = (totalSize - sizes [0]) / 2; // single item is centered + } break; + case Justification.Justified: int spaceBetween = sizes.Length > 1 ? (totalSize - totalItemsSize) / (sizes.Length - 1) : 0; int remainder = sizes.Length > 1 ? (totalSize - totalItemsSize) % (sizes.Length - 1) : 0; diff --git a/Terminal.Gui/View/Layout/PosDim.cs b/Terminal.Gui/View/Layout/PosDim.cs index 812b29b3e..37f2a5bf0 100644 --- a/Terminal.Gui/View/Layout/PosDim.cs +++ b/Terminal.Gui/View/Layout/PosDim.cs @@ -307,22 +307,22 @@ public class Pos /// Creates a object that tracks the Top (Y) position of the specified . /// The that depends on the other view. /// The that will be tracked. - public static Pos Top (View view) { return new PosView (view, Side.Y); } + public static Pos Top (View view) { return new PosView (view, Side.Top); } /// Creates a object that tracks the Top (Y) position of the specified . /// The that depends on the other view. /// The that will be tracked. - public static Pos Y (View view) { return new PosView (view, Side.Y); } + public static Pos Y (View view) { return new PosView (view, Side.Top); } /// Creates a object that tracks the Left (X) position of the specified . /// The that depends on the other view. /// The that will be tracked. - public static Pos Left (View view) { return new PosView (view, Side.X); } + public static Pos Left (View view) { return new PosView (view, Side.Left); } /// Creates a object that tracks the Left (X) position of the specified . /// The that depends on the other view. /// The that will be tracked. - public static Pos X (View view) { return new PosView (view, Side.X); } + public static Pos X (View view) { return new PosView (view, Side.Left); } /// /// Creates a object that tracks the Bottom (Y+Height) coordinate of the specified @@ -486,8 +486,8 @@ public class Pos public enum Side { - X = 0, - Y = 1, + Left = 0, + Top = 1, Right = 2, Bottom = 3 } @@ -503,8 +503,8 @@ public class Pos { string sideString = side switch { - Side.X => "x", - Side.Y => "y", + Side.Left => "x", + Side.Top => "y", Side.Right => "right", Side.Bottom => "bottom", _ => "unknown" @@ -522,8 +522,8 @@ public class Pos { return side switch { - Side.X => Target.Frame.X, - Side.Y => Target.Frame.Y, + Side.Left => Target.Frame.X, + Side.Top => Target.Frame.Y, Side.Right => Target.Frame.Right, Side.Bottom => Target.Frame.Bottom, _ => 0 @@ -718,7 +718,7 @@ public class Dim /// Creates a object that tracks the Height of the specified . /// The height of the other . /// The view that will be tracked. - public static Dim Height (View view) { return new DimView (view, Side.Height); } + public static Dim Height (View view) { return new DimView (view, Dimension.Height); } /// Adds a to a , yielding a new . /// The first to add. @@ -801,7 +801,7 @@ public class Dim /// Creates a object that tracks the Width of the specified . /// The width of the other . /// The view that will be tracked. - public static Dim Width (View view) { return new DimView (view, Side.Width); } + public static Dim Width (View view) { return new DimView (view, Dimension.Width); } /// /// Gets a dimension that is anchored to a certain point in the layout. @@ -932,7 +932,7 @@ public class Dim internal override int Anchor (int width) { return _function (); } } - public enum Side + public enum Dimension { Height = 0, Width = 1 @@ -940,9 +940,9 @@ public class Dim internal class DimView : Dim { - private readonly Side _side; + private readonly Dimension _side; - internal DimView (View view, Side side) + internal DimView (View view, Dimension side) { Target = view; _side = side; @@ -961,8 +961,8 @@ public class Dim string sideString = _side switch { - Side.Height => "Height", - Side.Width => "Width", + Dimension.Height => "Height", + Dimension.Width => "Width", _ => "unknown" }; @@ -973,8 +973,8 @@ public class Dim { return _side switch { - Side.Height => Target.Frame.Height, - Side.Width => Target.Frame.Width, + Dimension.Height => Target.Frame.Height, + Dimension.Width => Target.Frame.Width, _ => 0 }; } diff --git a/UnitTests/Drawing/JustifierTests.cs b/UnitTests/Drawing/JustifierTests.cs index 62f036aa0..37c50c506 100644 --- a/UnitTests/Drawing/JustifierTests.cs +++ b/UnitTests/Drawing/JustifierTests.cs @@ -81,24 +81,27 @@ public class JustifierTests (ITestOutputHelper output) } [Theory] - [InlineData (Justification.Left, new int [] { 10, 20, 30 }, 100, new int [] { 0, 10, 30 })] - [InlineData (Justification.Left, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 66 })] - [InlineData (Justification.Left, new int [] { 10 }, 101, new int [] { 0 })] - [InlineData (Justification.Left, new int [] { 10, 20 }, 101, new int [] { 0, 10 })] - [InlineData (Justification.Left, new int [] { 10, 20, 30 }, 101, new int [] { 0, 10, 30 })] - [InlineData (Justification.Left, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 60 })] - [InlineData (Justification.Left, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 100 })] + //[InlineData (Justification.Left, new int [] { 10, 20, 30 }, 100, new int [] { 0, 10, 30 })] + //[InlineData (Justification.Left, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 66 })] + //[InlineData (Justification.Left, new int [] { 10 }, 101, new int [] { 0 })] + //[InlineData (Justification.Left, new int [] { 10, 20 }, 101, new int [] { 0, 10 })] + //[InlineData (Justification.Left, new int [] { 10, 20, 30 }, 101, new int [] { 0, 10, 30 })] + //[InlineData (Justification.Left, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 60 })] + //[InlineData (Justification.Left, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 100 })] - [InlineData (Justification.Right, new int [] { 10, 20, 30 }, 100, new int [] { 40, 50, 70 })] - [InlineData (Justification.Right, new int [] { 33, 33, 33 }, 100, new int [] { 1, 34, 67 })] - [InlineData (Justification.Right, new int [] { 10 }, 101, new int [] { 91 })] - [InlineData (Justification.Right, new int [] { 10, 20 }, 101, new int [] { 71, 81 })] - [InlineData (Justification.Right, new int [] { 10, 20, 30 }, 101, new int [] { 41, 51, 71 })] - [InlineData (Justification.Right, new int [] { 10, 20, 30, 40 }, 101, new int [] { 1, 11, 31, 61 })] - [InlineData (Justification.Right, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 1, 11, 31, 61, 101 })] + //[InlineData (Justification.Right, new int [] { 10, 20, 30 }, 100, new int [] { 40, 50, 70 })] + //[InlineData (Justification.Right, new int [] { 33, 33, 33 }, 100, new int [] { 1, 34, 67 })] + //[InlineData (Justification.Right, new int [] { 10 }, 101, new int [] { 91 })] + //[InlineData (Justification.Right, new int [] { 10, 20 }, 101, new int [] { 71, 81 })] + //[InlineData (Justification.Right, new int [] { 10, 20, 30 }, 101, new int [] { 41, 51, 71 })] + //[InlineData (Justification.Right, new int [] { 10, 20, 30, 40 }, 101, new int [] { 1, 11, 31, 61 })] + //[InlineData (Justification.Right, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 1, 11, 31, 61, 101 })] - [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 100, new int [] { 20, 30, 50 })] - [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 99, new int [] { 0, 33, 66 })] + [InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 10, new int [] { 1, 3, 6 })] + [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 100, new int [] { 19, 30, 51 })] + [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 9, new int [] { 0, 3, 6 })] + [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 10, new int [] { 0, 4, 7 })] + [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 11, new int [] { 0, 4, 8 })] [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 66 })] [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 101, new int [] { 1, 34, 67 })] [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 102, new int [] { 1, 34, 67 })] @@ -108,48 +111,49 @@ public class JustifierTests (ITestOutputHelper output) [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 101, new int [] { 20, 30, 50 })] [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 60 })] [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 100 })] + [InlineData (Justification.Centered, new int [] { 3, 4, 5, 6 }, 25, new int [] { 2, 6, 11, 17 })] - [InlineData (Justification.Justified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] - [InlineData (Justification.Justified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] - [InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 30, 70 })] - [InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 31, 71 })] - [InlineData (Justification.Justified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] - [InlineData (Justification.Justified, new int [] { 11, 17, 23 }, 100, new int [] { 0, 36, 77 })] - [InlineData (Justification.Justified, new int [] { 1, 2, 3 }, 11, new int [] { 0, 4, 8 })] - [InlineData (Justification.Justified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - [InlineData (Justification.Justified, new int [] { 10 }, 101, new int [] { 0 })] - [InlineData (Justification.Justified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 9, 18 })] - [InlineData (Justification.Justified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 8, 16 })] - [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 18, new int [] { 0, 3, 7, 12 })] - [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 19, new int [] { 0, 4, 8, 13 })] - [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 20, new int [] { 0, 4, 9, 14 })] - [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 21, new int [] { 0, 4, 9, 15 })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 22, new int [] { 0, 8, 14, 19 })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 23, new int [] { 0, 8, 15, 20, })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 24, new int [] { 0, 8, 15, 21 })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 25, new int [] { 0, 9, 16, 22 })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 26, new int [] { 0, 9, 17, 23 })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 31, new int [] { 0, 11, 20, 28 })] + //[InlineData (Justification.Justified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] + //[InlineData (Justification.Justified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] + //[InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 30, 70 })] + //[InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 31, 71 })] + //[InlineData (Justification.Justified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] + //[InlineData (Justification.Justified, new int [] { 11, 17, 23 }, 100, new int [] { 0, 36, 77 })] + //[InlineData (Justification.Justified, new int [] { 1, 2, 3 }, 11, new int [] { 0, 4, 8 })] + //[InlineData (Justification.Justified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + //[InlineData (Justification.Justified, new int [] { 10 }, 101, new int [] { 0 })] + //[InlineData (Justification.Justified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 9, 18 })] + //[InlineData (Justification.Justified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 8, 16 })] + //[InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 18, new int [] { 0, 3, 7, 12 })] + //[InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 19, new int [] { 0, 4, 8, 13 })] + //[InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 20, new int [] { 0, 4, 9, 14 })] + //[InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 21, new int [] { 0, 4, 9, 15 })] + //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 22, new int [] { 0, 8, 14, 19 })] + //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 23, new int [] { 0, 8, 15, 20, })] + //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 24, new int [] { 0, 8, 15, 21 })] + //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 25, new int [] { 0, 9, 16, 22 })] + //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 26, new int [] { 0, 9, 17, 23 })] + //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 31, new int [] { 0, 11, 20, 28 })] - [InlineData (Justification.LeftJustified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 11, 70 })] - [InlineData (Justification.LeftJustified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] - [InlineData (Justification.LeftJustified, new int [] { 10 }, 101, new int [] { 0 })] - [InlineData (Justification.LeftJustified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - [InlineData (Justification.LeftJustified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 11, 71 })] - [InlineData (Justification.LeftJustified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 32, 61 })] - [InlineData (Justification.LeftJustified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 32, 63, 101 })] - [InlineData (Justification.LeftJustified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 4, 18 })] - [InlineData (Justification.LeftJustified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 4, 16 })] + //[InlineData (Justification.LeftJustified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 11, 70 })] + //[InlineData (Justification.LeftJustified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] + //[InlineData (Justification.LeftJustified, new int [] { 10 }, 101, new int [] { 0 })] + //[InlineData (Justification.LeftJustified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + //[InlineData (Justification.LeftJustified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 11, 71 })] + //[InlineData (Justification.LeftJustified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 32, 61 })] + //[InlineData (Justification.LeftJustified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 32, 63, 101 })] + //[InlineData (Justification.LeftJustified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 4, 18 })] + //[InlineData (Justification.LeftJustified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 4, 16 })] - [InlineData (Justification.RightJustified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 49, 70 })] - [InlineData (Justification.RightJustified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 67 })] - [InlineData (Justification.RightJustified, new int [] { 10 }, 101, new int [] { 0 })] - [InlineData (Justification.RightJustified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - [InlineData (Justification.RightJustified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 50, 71 })] - [InlineData (Justification.RightJustified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 9, 30, 61 })] - [InlineData (Justification.RightJustified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 8, 29, 60, 101 })] - [InlineData (Justification.RightJustified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 14, 18 })] - [InlineData (Justification.RightJustified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 11, 16 })] + //[InlineData (Justification.RightJustified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 49, 70 })] + //[InlineData (Justification.RightJustified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 67 })] + //[InlineData (Justification.RightJustified, new int [] { 10 }, 101, new int [] { 0 })] + //[InlineData (Justification.RightJustified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + //[InlineData (Justification.RightJustified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 50, 71 })] + //[InlineData (Justification.RightJustified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 9, 30, 61 })] + //[InlineData (Justification.RightJustified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 8, 29, 60, 101 })] + //[InlineData (Justification.RightJustified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 14, 18 })] + //[InlineData (Justification.RightJustified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 11, 16 })] public void TestJustifications (Justification justification, int [] sizes, int totalSize, int [] expected) { From 292ebfb6380586c31c936534d90de9f66818fc93 Mon Sep 17 00:00:00 2001 From: Tig Date: Sat, 20 Apr 2024 18:30:01 -0600 Subject: [PATCH 04/11] WIP --- Terminal.Gui/Drawing/Justification.cs | 208 ++++++++----- UnitTests/Drawing/JustifierTests.cs | 408 +++++++++++++++++++------- 2 files changed, 435 insertions(+), 181 deletions(-) diff --git a/Terminal.Gui/Drawing/Justification.cs b/Terminal.Gui/Drawing/Justification.cs index 557d35855..5038c31d2 100644 --- a/Terminal.Gui/Drawing/Justification.cs +++ b/Terminal.Gui/Drawing/Justification.cs @@ -6,52 +6,70 @@ namespace Terminal.Gui; public enum Justification { /// - /// The items will be left-justified. + /// The items will be aligned to the left. + /// The items will be arranged such that there is no more than space between each. /// + /// + /// + /// 111 2222 33333 + /// + /// Left, /// - /// The items will be right-justified. + /// The items will be aligned to the right. + /// The items will be arranged such that there is no more than space between each. /// + /// + /// + /// 111 2222 33333 + /// + /// Right, /// - /// The items will be arranged such that there is no more than 1 space between them. The group will be centered in the container. + /// The group will be centered in the container. + /// If centering is not possible, the group will be left-justified. + /// The items will be arranged such that there is no more than space between each. /// + /// + /// + /// 111 2222 33333 + /// + /// Centered, /// /// The items will be justified. Space will be added between the items such that the first item /// is at the start and the right side of the last item against the end. + /// The items will be arranged such that there is no more than space between each. /// /// /// - /// 111 2222 33333 + /// 111 2222 33333 /// /// Justified, /// - /// The items will be left-justified. The first item will be at the start and the last item will be at the end. - /// Those in between will be tight against the right item. - /// - /// - /// - /// 111 2222 33333 - /// - /// - RightJustified, - - /// - /// The items will be left-justified. The first item will be at the start and the last item will be at the end. - /// Those in between will be tight against the right item. + /// The first item will be aligned to the left and the remaining will aligned to the right with no more than between each. /// /// /// /// 111 2222 33333 /// /// - LeftJustified + OneLeftRestRight, + + /// + /// The last item will be aligned to right and the remaining will aligned to the left with no more than between each. + /// + /// + /// + /// 111 2222 33333 + /// + /// + OneRightRestLeft } /// @@ -59,6 +77,11 @@ public enum Justification /// public class Justifier { + /// + /// Gets or sets the maximum space between items. The default is 0. For text, this is usually 1. + /// + public int MaxSpaceBetweenItems { get; set; } = 0; + /// /// Justifies the within a container wide based on the specified /// . @@ -67,9 +90,12 @@ public class Justifier /// /// /// - public static int [] Justify (int [] sizes, Justification justification, int totalSize) + public int [] Justify (int [] sizes, Justification justification, int totalSize) { - var positions = new int [sizes.Length]; + if (sizes.Length == 0) + { + return new int []{}; + } int totalItemsSize = sizes.Sum (); if (totalItemsSize > totalSize) @@ -77,6 +103,21 @@ public class Justifier 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 + if (totalItemsSize >= totalSize) + { + spaces = 0; + } + else if (totalItemsAndSpaces > totalSize) + { + spaces = totalSize - totalItemsSize; + } + + switch (justification) { case Justification.Left: @@ -89,13 +130,21 @@ public class Justifier throw new ArgumentException ("The size of an item cannot be negative."); } - positions [i] = currentPosition; - currentPosition += sizes [i]; + if (i == 0) + { + positions [0] = 0; // first item position + continue; + } + + var spaceBefore = spaces-- > 0 ? MaxSpaceBetweenItems : 0; + + // subsequent items are placed one space after the previous item + positions [i] = positions [i - 1] + sizes [i - 1] + spaceBefore; } break; case Justification.Right: - currentPosition = totalSize - totalItemsSize; + currentPosition = Math.Max (0, totalSize - totalItemsSize - spaces); for (var i = 0; i < sizes.Length; i++) { @@ -104,8 +153,10 @@ public class Justifier throw new ArgumentException ("The size of an item cannot be negative."); } + var spaceBefore = spaces-- > 0 ? MaxSpaceBetweenItems : 0; + positions [i] = currentPosition; - currentPosition += sizes [i]; + currentPosition += sizes [i] + spaceBefore; } break; @@ -113,44 +164,35 @@ public class Justifier case Justification.Centered: if (sizes.Length > 1) { - totalItemsSize = sizes.Sum (); // total size of items - int totalGaps = sizes.Length - 1; // total gaps (0 or 1 space) - int totalItemsAndSpaces = totalItemsSize + totalGaps; // total size of items and spaces + // remaining space to be distributed before first and after the items + int remainingSpace = Math.Max(0, totalSize - totalItemsSize - spaces); - int spaces = totalGaps; - - if (totalItemsSize >= totalSize) + for (var i = 0; i < sizes.Length; i++) { - spaces = 0; - } - else if (totalItemsAndSpaces > totalSize) - { - spaces = totalItemsAndSpaces - totalSize; - } - - int remainingSpace = Math.Max(0, totalSize - totalItemsSize - spaces); // remaining space to be distributed before and after the items - int spaceBefore = remainingSpace / 2; // space before the items - - positions [0] = spaceBefore; // first item position - for (var i = 1; i < sizes.Length; i++) - { - int aSpace = 0; - if (spaces > 0) + if (sizes [i] < 0) { - spaces--; - aSpace = 1; + throw new ArgumentException ("The size of an item cannot be negative."); } + + if (i == 0) + { + positions [i] = remainingSpace / 2; // first item position + + continue; + } + + var spaceBefore = spaces-- > 0 ? MaxSpaceBetweenItems : 0; + // subsequent items are placed one space after the previous item - positions [i] = positions [i - 1] + sizes [i - 1] + aSpace; - } - // Adjust the last position if there is an extra space - if (positions [sizes.Length - 1] + sizes [sizes.Length - 1] > totalSize) - { - positions [sizes.Length - 1]--; + positions [i] = positions [i - 1] + sizes [i - 1] + spaceBefore; } } else if (sizes.Length == 1) { + if (sizes [0] < 0) + { + throw new ArgumentException ("The size of an item cannot be negative."); + } positions [0] = (totalSize - sizes [0]) / 2; // single item is centered } break; @@ -172,46 +214,74 @@ public class Justifier } break; - case Justification.LeftJustified: + /// 111 2222 33333 + case Justification.OneRightRestLeft: if (sizes.Length > 1) { - int spaceBetweenLeft = totalSize - sizes.Sum () + 1; // +1 for the extra space currentPosition = 0; - for (var i = 0; i < sizes.Length - 1; i++) + for (var i = 0; i < sizes.Length; i++) { if (sizes [i] < 0) { throw new ArgumentException ("The size of an item cannot be negative."); } - positions [i] = currentPosition; - currentPosition += sizes [i] + 1; // +1 for the extra space + + if (i < sizes.Length - 1) + { + var spaceBefore = spaces-- > 0 ? MaxSpaceBetweenItems : 0; + + positions [i] = currentPosition; + currentPosition += sizes [i] + spaceBefore; + } } positions [sizes.Length - 1] = totalSize - sizes [sizes.Length - 1]; } else if (sizes.Length == 1) { - positions [0] = 0; + if (sizes [0] < 0) + { + throw new ArgumentException ("The size of an item cannot be negative."); + } + positions [0] = totalSize - sizes [0]; // single item is flush right } break; - case Justification.RightJustified: + /// 111 2222 33333 + case Justification.OneLeftRestRight: if (sizes.Length > 1) { - totalItemsSize = sizes.Sum (); - int totalSpaces = totalSize - totalItemsSize; - int bigSpace = totalSpaces - (sizes.Length - 2); + currentPosition = 0; + positions [0] = currentPosition; // first item is flush left - positions [0] = 0; // first item is flush left - positions [1] = sizes [0] + bigSpace; // second item has the big space before it - - // remaining items have one space between them - for (var i = 2; i < sizes.Length; i++) + for (var i = sizes.Length - 1 ; i >= 0; i--) { - positions [i] = positions [i - 1] + sizes [i - 1] + 1; + if (sizes [i] < 0) + { + throw new ArgumentException ("The size of an item cannot be negative."); + } + + if (i == sizes.Length - 1) + { + // start at right + currentPosition = totalSize - sizes [i]; + positions [i] = currentPosition; + } + + if (i < sizes.Length - 1 && i > 0) + { + var spaceBefore = spaces-- > 0 ? MaxSpaceBetweenItems : 0; + + positions [i] = currentPosition - sizes [i] - spaceBefore; + currentPosition -= sizes [i + 1]; + } } } else if (sizes.Length == 1) { + if (sizes [0] < 0) + { + throw new ArgumentException ("The size of an item cannot be negative."); + } positions [0] = 0; // single item is flush left } break; diff --git a/UnitTests/Drawing/JustifierTests.cs b/UnitTests/Drawing/JustifierTests.cs index 37c50c506..de7ae2e2b 100644 --- a/UnitTests/Drawing/JustifierTests.cs +++ b/UnitTests/Drawing/JustifierTests.cs @@ -10,77 +10,199 @@ public class JustifierTests (ITestOutputHelper output) private readonly ITestOutputHelper _output = output; - [Fact] - public void TestLeftJustification () + public static IEnumerable JustificationEnumValues () { - int [] sizes = { 10, 20, 30 }; - var positions = Justifier.Justify (sizes, Justification.Left, 100); - Assert.Equal (new List { 0, 10, 30 }, positions); - } - - [Fact] - public void TestRightJustification () - { - int [] sizes = { 10, 20, 30 }; - var positions = Justifier.Justify (sizes, Justification.Right, 100); - Assert.Equal (new List { 40, 50, 70 }, positions); - } - - [Fact] - public void TestCenterJustification () - { - int [] sizes = { 10, 20, 30 }; - var positions = Justifier.Justify (sizes, Justification.Centered, 100); - Assert.Equal (new List { 20, 30, 50 }, positions); - } - - [Fact] - public void TestJustifiedJustification () - { - int [] sizes = { 10, 20, 30 }; - var positions = Justifier.Justify (sizes, Justification.Justified, 100); - Assert.Equal (new List { 0, 30, 70 }, positions); - } - - [Fact] - public void TestNoItems () - { - int [] sizes = { }; - var positions = Justifier.Justify (sizes, Justification.Left, 100); - Assert.Equal (new int [] { }, positions); - } - - [Fact] - public void TestTenItems () - { - int [] sizes = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }; - var positions = Justifier.Justify (sizes, Justification.Left, 100); - Assert.Equal (new int [] { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 }, positions); - } - - [Fact] - public void TestZeroLengthItems () - { - int [] sizes = { 0, 0, 0 }; - var positions = Justifier.Justify (sizes, Justification.Left, 100); - Assert.Equal (new int [] { 0, 0, 0 }, positions); - } - - [Fact] - public void TestLongItems () - { - int [] sizes = { 1000, 2000, 3000 }; - Assert.Throws (() => Justifier.Justify (sizes, Justification.Left, 100)); - } - - [Fact] - public void TestNegativeLengths () - { - int [] sizes = { -10, -20, -30 }; - Assert.Throws (() => Justifier.Justify (sizes, Justification.Left, 100)); + foreach (var number in Enum.GetValues (typeof (Justification))) + { + yield return new object [] { number }; + } } [Theory] + [MemberData (nameof (JustificationEnumValues))] + public void NoItems_Works (Justification justification) + { + int [] sizes = { }; + var positions = new Justifier ().Justify (sizes, justification, 100); + 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 int [] { -10, 20, 30 }, justification, 100)); + Assert.Throws (() => new Justifier ().Justify (new int [] { 10, -20, 30 }, justification, 100)); + Assert.Throws (() => new Justifier ().Justify (new int [] { 10, 20, -30 }, justification, 100)); + } + + [Theory] + [InlineData (Justification.Left, new int [] { 0 }, 1, new int [] { 0 })] + [InlineData (Justification.Left, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] + [InlineData (Justification.Left, new int [] { 0, 0, 0 }, 1, new int [] { 0, 1, 1 })] + [InlineData (Justification.Left, new int [] { 1 }, 1, new int [] { 0 })] + [InlineData (Justification.Left, new int [] { 1 }, 2, new int [] { 0 })] + [InlineData (Justification.Left, new int [] { 1 }, 3, new int [] { 0 })] + [InlineData (Justification.Left, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] + [InlineData (Justification.Left, new int [] { 1, 1 }, 3, new int [] { 0, 2 })] + [InlineData (Justification.Left, new int [] { 1, 1 }, 4, new int [] { 0, 2 })] + [InlineData (Justification.Left, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] + [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] + [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 7, new int [] { 0, 2, 4 })] + [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 10, new int [] { 0, 2, 5 })] + [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 11, new int [] { 0, 2, 5 })] + [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 12, new int [] { 0, 2, 5 })] + [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 13, new int [] { 0, 2, 5 })] + [InlineData (Justification.Left, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] + [InlineData (Justification.Left, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 2, 4, 7 })] + [InlineData (Justification.Left, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] + [InlineData (Justification.Left, new int [] { 10 }, 101, new int [] { 0 })] + [InlineData (Justification.Left, new int [] { 10, 20 }, 101, new int [] { 0, 11 })] + [InlineData (Justification.Left, new int [] { 10, 20, 30 }, 100, new int [] { 0, 11, 32 })] + [InlineData (Justification.Left, new int [] { 10, 20, 30 }, 101, new int [] { 0, 11, 32 })] + [InlineData (Justification.Left, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] + [InlineData (Justification.Left, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] + + [InlineData (Justification.Right, new int [] { 0 }, 1, new int [] { 1 })] + [InlineData (Justification.Right, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] + [InlineData (Justification.Right, new int [] { 0, 0, 0 }, 1, new int [] { 0, 1, 1 })] + [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] + [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 7, new int [] { 0, 2, 4 })] + [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 10, new int [] { 2, 4, 7 })] + [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 11, new int [] { 3, 5, 8 })] + [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 12, new int [] { 4, 6, 9 })] + [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 13, new int [] { 5, 7, 10 })] + [InlineData (Justification.Right, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] + [InlineData (Justification.Right, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 2, 4, 7 })] + [InlineData (Justification.Right, new int [] { 10, 20, 30 }, 100, new int [] { 38, 49, 70 })] + [InlineData (Justification.Right, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] + [InlineData (Justification.Right, new int [] { 10 }, 101, new int [] { 91 })] + [InlineData (Justification.Right, new int [] { 10, 20 }, 101, new int [] { 70, 81 })] + [InlineData (Justification.Right, new int [] { 10, 20, 30 }, 101, new int [] { 39, 50, 71 })] + [InlineData (Justification.Right, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] + [InlineData (Justification.Right, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] + + [InlineData (Justification.Centered, new int [] { 0 }, 1, new int [] { 0 })] + [InlineData (Justification.Centered, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] + [InlineData (Justification.Centered, new int [] { 0, 0, 0 }, 1, new int [] { 0, 1, 1 })] + [InlineData (Justification.Centered, new int [] { 1 }, 1, new int [] { 0 })] + [InlineData (Justification.Centered, new int [] { 1 }, 2, new int [] { 0 })] + [InlineData (Justification.Centered, new int [] { 1 }, 3, new int [] { 1 })] + [InlineData (Justification.Centered, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] + [InlineData (Justification.Centered, new int [] { 1, 1 }, 3, new int [] { 0, 2 })] + [InlineData (Justification.Centered, new int [] { 1, 1 }, 4, new int [] { 0, 2 })] + [InlineData (Justification.Centered, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] + [InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] + [InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 7, new int [] { 0, 2, 4 })] + [InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 10, new int [] { 1, 3, 6 })] + [InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 11, new int [] { 1, 3, 6 })] + [InlineData (Justification.Centered, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] + [InlineData (Justification.Centered, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 2, 4, 7 })] + [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 9, new int [] { 0, 3, 6 })] + [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 10, new int [] { 0, 4, 7 })] + [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 11, new int [] { 0, 4, 8 })] + [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 12, new int [] { 0, 4, 8 })] + [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 13, new int [] { 1, 5, 9 })] + [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] + [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 101, new int [] { 0, 34, 68 })] + [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 102, new int [] { 0, 34, 68 })] + [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 103, new int [] { 1, 35, 69 })] + [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 104, new int [] { 1, 35, 69 })] + [InlineData (Justification.Centered, new int [] { 10 }, 101, new int [] { 45 })] + [InlineData (Justification.Centered, new int [] { 10, 20 }, 101, new int [] { 35, 46 })] + [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 100, new int [] { 19, 30, 51 })] + [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 101, new int [] { 19, 30, 51 })] + [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40 }, 100, new int [] { 0, 10, 30, 60 })] + [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] + [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] + [InlineData (Justification.Centered, new int [] { 3, 4, 5, 6 }, 25, new int [] { 2, 6, 11, 17 })] + + [InlineData (Justification.Justified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] + [InlineData (Justification.Justified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] + [InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 30, 70 })] + [InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 31, 71 })] + [InlineData (Justification.Justified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] + [InlineData (Justification.Justified, new int [] { 11, 17, 23 }, 100, new int [] { 0, 36, 77 })] + [InlineData (Justification.Justified, new int [] { 1, 2, 3 }, 11, new int [] { 0, 4, 8 })] + [InlineData (Justification.Justified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + [InlineData (Justification.Justified, new int [] { 10 }, 101, new int [] { 0 })] + [InlineData (Justification.Justified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 9, 18 })] + [InlineData (Justification.Justified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 8, 16 })] + [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 18, new int [] { 0, 3, 7, 12 })] + [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 19, new int [] { 0, 4, 8, 13 })] + [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 20, new int [] { 0, 4, 9, 14 })] + [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 21, new int [] { 0, 4, 9, 15 })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 22, new int [] { 0, 8, 14, 19 })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 23, new int [] { 0, 8, 15, 20, })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 24, new int [] { 0, 8, 15, 21 })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 25, new int [] { 0, 9, 16, 22 })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 26, new int [] { 0, 9, 17, 23 })] + [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 31, new int [] { 0, 11, 20, 28 })] + + [InlineData (Justification.OneRightRestLeft, new int [] { 0 }, 1, new int [] { 0 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 0, 0, 0 }, 1, new int [] { 0, 1, 1 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 1, new int [] { 0 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 2, new int [] { 0 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 3, new int [] { 0 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 3, new int [] { 0, 2 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 4, new int [] { 0, 3 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 7, new int [] { 0, 2, 4 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 8, new int [] { 0, 2, 5 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 9, new int [] { 0, 2, 6 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 10, new int [] { 0, 2, 7 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 11, new int [] { 0, 2, 8 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 2, 4, 7 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 3, 3, 3 }, 21, new int [] { 0, 4, 18 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 3, 4, 5 }, 21, new int [] { 0, 4, 16 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 10 }, 101, new int [] { 0 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30 }, 100, new int [] { 0, 11, 70 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30 }, 101, new int [] { 0, 11, 71 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] + [InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] + + //[InlineData (Justification.SplitLeft, new int [] { 10, 20, 30 }, 100, new int [] { 0, 49, 70 })] + //[InlineData (Justification.SplitLeft, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 67 })] + //[InlineData (Justification.SplitLeft, new int [] { 10 }, 101, new int [] { 0 })] + //[InlineData (Justification.SplitLeft, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + //[InlineData (Justification.SplitLeft, new int [] { 10, 20, 30 }, 101, new int [] { 0, 50, 71 })] + //[InlineData (Justification.SplitLeft, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 9, 30, 61 })] + //[InlineData (Justification.SplitLeft, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 8, 29, 60, 101 })] + //[InlineData (Justification.SplitLeft, new int [] { 3, 3, 3 }, 21, new int [] { 0, 14, 18 })] + //[InlineData (Justification.SplitLeft, new int [] { 3, 4, 5 }, 21, new int [] { 0, 11, 16 })] + + public void TestJustifications_1Space (Justification justification, int [] sizes, int totalSize, int [] expected) + { + var positions = new Justifier () { MaxSpaceBetweenItems = 1 }.Justify (sizes, justification, totalSize); + AssertJustification (justification, sizes, totalSize, positions, expected); + } + + [Theory] + //[InlineData (Justification.Left, new int [] { 0 }, 1, new int [] { 0 })] + //[InlineData (Justification.Left, new int [] { 0, 0 }, 1, new int [] { 0, 0 })] + //[InlineData (Justification.Left, new int [] { 0, 0, 0 }, 1, new int [] { 0, 0, 0 })] + //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] + //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 7, new int [] { 0, 1, 3 })] + //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 10, new int [] { 0, 1, 3 })] + //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 11, new int [] { 0, 1, 3 })] + //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 12, new int [] { 0, 1, 3 })] + //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 13, new int [] { 0, 1, 3 })] + //[InlineData (Justification.Left, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] + //[InlineData (Justification.Left, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 1, 3, 6 })] //[InlineData (Justification.Left, new int [] { 10, 20, 30 }, 100, new int [] { 0, 10, 30 })] //[InlineData (Justification.Left, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 66 })] //[InlineData (Justification.Left, new int [] { 10 }, 101, new int [] { 0 })] @@ -89,6 +211,17 @@ public class JustifierTests (ITestOutputHelper output) //[InlineData (Justification.Left, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 60 })] //[InlineData (Justification.Left, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 100 })] + //[InlineData (Justification.Right, new int [] { 0 }, 1, new int [] { 1 })] + //[InlineData (Justification.Right, new int [] { 0, 0 }, 1, new int [] { 1, 1 })] + //[InlineData (Justification.Right, new int [] { 0, 0, 0 }, 1, new int [] { 1, 1, 1 })] + //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] + //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 7, new int [] { 1, 2, 4 })] + //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 10, new int [] { 4, 5, 7 })] + //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 11, new int [] { 5, 6, 8 })] + //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 12, new int [] { 6, 7, 9 })] + //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 13, new int [] { 7, 8, 10 })] + //[InlineData (Justification.Right, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] + //[InlineData (Justification.Right, new int [] { 1, 2, 3, 4 }, 11, new int [] { 1, 2, 4, 7 })] //[InlineData (Justification.Right, new int [] { 10, 20, 30 }, 100, new int [] { 40, 50, 70 })] //[InlineData (Justification.Right, new int [] { 33, 33, 33 }, 100, new int [] { 1, 34, 67 })] //[InlineData (Justification.Right, new int [] { 10 }, 101, new int [] { 91 })] @@ -97,21 +230,30 @@ public class JustifierTests (ITestOutputHelper output) //[InlineData (Justification.Right, new int [] { 10, 20, 30, 40 }, 101, new int [] { 1, 11, 31, 61 })] //[InlineData (Justification.Right, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 1, 11, 31, 61, 101 })] - [InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 10, new int [] { 1, 3, 6 })] - [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 100, new int [] { 19, 30, 51 })] - [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 9, new int [] { 0, 3, 6 })] - [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 10, new int [] { 0, 4, 7 })] - [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 11, new int [] { 0, 4, 8 })] - [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 66 })] - [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 101, new int [] { 1, 34, 67 })] - [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 102, new int [] { 1, 34, 67 })] - [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 104, new int [] { 2, 35, 68 })] - [InlineData (Justification.Centered, new int [] { 10 }, 101, new int [] { 45 })] - [InlineData (Justification.Centered, new int [] { 10, 20 }, 101, new int [] { 35, 45 })] - [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 101, new int [] { 20, 30, 50 })] - [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 60 })] - [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 100 })] - [InlineData (Justification.Centered, new int [] { 3, 4, 5, 6 }, 25, new int [] { 2, 6, 11, 17 })] + //[InlineData (Justification.Centered, new int [] { 1 }, 1, new int [] { 0 })] + //[InlineData (Justification.Centered, new int [] { 1 }, 2, new int [] { 0 })] + //[InlineData (Justification.Centered, new int [] { 1 }, 3, new int [] { 1 })] + //[InlineData (Justification.Centered, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] + //[InlineData (Justification.Centered, new int [] { 1, 1 }, 3, new int [] { 0, 1 })] + //[InlineData (Justification.Centered, new int [] { 1, 1 }, 4, new int [] { 1, 2 })] + //[InlineData (Justification.Centered, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] + //[InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] + //[InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 7, new int [] { 0, 1, 3 })] + //[InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 10, new int [] { 2, 3, 5 })] + //[InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 11, new int [] { 2, 3, 5 })] + //[InlineData (Justification.Centered, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] + //[InlineData (Justification.Centered, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 1, 3, 6 })] + //[InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 9, new int [] { 0, 3, 6 })] + //[InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 10, new int [] { 0, 3, 6 })] + //[InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 11, new int [] { 1, 4, 7 })] + //[InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 12, new int [] { 1, 4, 7 })] + //[InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 13, new int [] { 2, 5, 8 })] + //[InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 66 })] + //[InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 101, new int [] { 1, 34, 67 })] + //[InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 102, new int [] { 1, 34, 67 })] + //[InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 103, new int [] { 2, 35, 68 })] + //[InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 104, new int [] { 2, 35, 68 })] + //[InlineData (Justification.Centered, new int [] { 3, 4, 5, 6 }, 25, new int [] { 3, 6, 10, 15 })] //[InlineData (Justification.Justified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] //[InlineData (Justification.Justified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] @@ -135,29 +277,70 @@ public class JustifierTests (ITestOutputHelper output) //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 26, new int [] { 0, 9, 17, 23 })] //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 31, new int [] { 0, 11, 20, 28 })] - //[InlineData (Justification.LeftJustified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 11, 70 })] - //[InlineData (Justification.LeftJustified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] - //[InlineData (Justification.LeftJustified, new int [] { 10 }, 101, new int [] { 0 })] - //[InlineData (Justification.LeftJustified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - //[InlineData (Justification.LeftJustified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 11, 71 })] - //[InlineData (Justification.LeftJustified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 32, 61 })] - //[InlineData (Justification.LeftJustified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 32, 63, 101 })] - //[InlineData (Justification.LeftJustified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 4, 18 })] - //[InlineData (Justification.LeftJustified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 4, 16 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 0 }, 1, new int [] { 0 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 0, 0, 0 }, 1, new int [] { 0, 0, 1 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 1, new int [] { 0 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 2, new int [] { 0 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 3, new int [] { 0 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 3, new int [] { 0, 2 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 4, new int [] { 0, 3 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 7, new int [] { 0, 1, 4 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 8, new int [] { 0, 1, 5 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 9, new int [] { 0, 1, 6 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 10, new int [] { 0, 1, 7 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 11, new int [] { 0, 1, 8 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 1, 3, 7 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3, 4 }, 12, new int [] { 0, 1, 3, 8 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 3, 3, 3 }, 21, new int [] { 0, 3, 18 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 3, 4, 5 }, 21, new int [] { 0, 3, 16 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 67 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 10 }, 101, new int [] { 0 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30 }, 100, new int [] { 0, 10, 70 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30 }, 101, new int [] { 0, 10, 71 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 61 })] + //[InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 101, })] - //[InlineData (Justification.RightJustified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 49, 70 })] - //[InlineData (Justification.RightJustified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 67 })] - //[InlineData (Justification.RightJustified, new int [] { 10 }, 101, new int [] { 0 })] - //[InlineData (Justification.RightJustified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - //[InlineData (Justification.RightJustified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 50, 71 })] - //[InlineData (Justification.RightJustified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 9, 30, 61 })] - //[InlineData (Justification.RightJustified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 8, 29, 60, 101 })] - //[InlineData (Justification.RightJustified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 14, 18 })] - //[InlineData (Justification.RightJustified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 11, 16 })] - public void TestJustifications (Justification justification, int [] sizes, int totalSize, int [] expected) + [InlineData (Justification.OneLeftRestRight, new int [] { 0 }, 1, new int [] { 0 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 0, 0, 0 }, 1, new int [] { 0, 1, 1 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1 }, 1, new int [] { 0 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1 }, 2, new int [] { 1 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1 }, 3, new int [] { 2 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1, 1 }, 3, new int [] { 0, 2 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1, 1 }, 4, new int [] { 0, 3 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 7, new int [] { 0, 2, 4 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 8, new int [] { 0, 3, 5 })] + [InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 9, new int [] { 0, 4, 6 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 10, new int [] { 0, 1, 7 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 11, new int [] { 0, 1, 8 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 1, 3, 7 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3, 4 }, 12, new int [] { 0, 1, 3, 8 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 3, 3, 3 }, 21, new int [] { 0, 3, 18 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 3, 4, 5 }, 21, new int [] { 0, 3, 16 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 67 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 10 }, 101, new int [] { 0 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 10, 20, 30 }, 100, new int [] { 0, 10, 70 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 10, 20, 30 }, 101, new int [] { 0, 10, 71 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 61 })] + //[InlineData (Justification.OneLeftRestRight, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 101, })] + + + + public void TestJustifications_0Space (Justification justification, int [] sizes, int totalSize, int [] expected) { - var positions = Justifier.Justify (sizes, justification, totalSize); + var positions = new Justifier () { MaxSpaceBetweenItems = 0 }.Justify (sizes, justification, totalSize); AssertJustification (justification, sizes, totalSize, positions, expected); } @@ -173,11 +356,11 @@ public class JustifierTests (ITestOutputHelper output) _output.WriteLine ($"Actual: {RenderJustification (justification, sizes, totalSize, positions)}"); } - if (!expected.SequenceEqual(positions)) + if (!expected.SequenceEqual (positions)) { _output.WriteLine ($"Expected: {RenderJustification (justification, sizes, totalSize, expected)}"); _output.WriteLine ($"Actual: {RenderJustification (justification, sizes, totalSize, positions)}"); - Assert.Fail(" Expected and actual do not match"); + Assert.Fail (" Expected and actual do not match"); } } @@ -201,19 +384,20 @@ public class JustifierTests (ITestOutputHelper output) var items = new char [totalSize]; for (int position = 0; position < positions.Length; position++) { - try + // try { - for (int j = 0; j < sizes [position]; j++) + for (int j = 0; j < sizes [position] && positions [position] + j < totalSize; j++) { items [positions [position] + j] = (position + 1).ToString () [0]; } - } catch(Exception e) - { - output.AppendLine ($"{e.Message} - position = {position}, positions[{position}]: {positions[position]}, sizes[{position}]: {sizes[position]}, totalSize: {totalSize}"); - output.Append (new string (items).Replace ('\0', ' ')); - - Assert.Fail(e.Message + output.ToString ()); } + //catch (Exception e) + //{ + // output.AppendLine ($"{e.Message} - position = {position}, positions[{position}]: {positions [position]}, sizes[{position}]: {sizes [position]}, totalSize: {totalSize}"); + // output.Append (new string (items).Replace ('\0', ' ')); + + // Assert.Fail (e.Message + output.ToString ()); + //} } output.Append (new string (items).Replace ('\0', ' ')); From 3f7159256c3288010fb2f2386e63ee1018476f8c Mon Sep 17 00:00:00 2001 From: Tig Date: Sat, 20 Apr 2024 23:24:18 -0600 Subject: [PATCH 05/11] Finished Justifier --- Terminal.Gui/Drawing/Justification.cs | 140 +++--- UnitTests/Drawing/JustifierTests.cs | 616 +++++++++++++------------- 2 files changed, 395 insertions(+), 361 deletions(-) diff --git a/Terminal.Gui/Drawing/Justification.cs b/Terminal.Gui/Drawing/Justification.cs index 5038c31d2..9109c6471 100644 --- a/Terminal.Gui/Drawing/Justification.cs +++ b/Terminal.Gui/Drawing/Justification.cs @@ -1,75 +1,83 @@ namespace Terminal.Gui; /// -/// Controls how items are justified within a container. Used by . +/// Controls how the justifies items within a container. /// public enum Justification { /// /// The items will be aligned to the left. - /// The items will be arranged such that there is no more than space between each. + /// Set to to ensure at least one space between + /// each item. /// /// - /// - /// 111 2222 33333 - /// + /// + /// 111 2222 33333 + /// /// Left, /// /// The items will be aligned to the right. - /// The items will be arranged such that there is no more than space between each. + /// Set to to ensure at least one space between + /// each item. /// /// - /// - /// 111 2222 33333 - /// + /// + /// 111 2222 33333 + /// /// Right, /// /// The group will be centered in the container. /// If centering is not possible, the group will be left-justified. - /// The items will be arranged such that there is no more than space between each. + /// Set to to ensure at least one space between + /// each item. /// /// - /// - /// 111 2222 33333 - /// + /// + /// 111 2222 33333 + /// /// Centered, /// /// The items will be justified. Space will be added between the items such that the first item /// is at the start and the right side of the last item against the end. - /// The items will be arranged such that there is no more than space between each. + /// Set to to ensure at least one space between + /// each item. /// /// - /// - /// 111 2222 33333 - /// + /// + /// 111 2222 33333 + /// /// Justified, /// - /// The first item will be aligned to the left and the remaining will aligned to the right with no more than between each. + /// The first item will be aligned to the left and the remaining will aligned to the right. + /// Set to to ensure at least one space between + /// each item. /// /// - /// - /// 111 2222 33333 - /// + /// + /// 111 2222 33333 + /// /// - OneLeftRestRight, + FirstLeftRestRight, /// - /// The last item will be aligned to right and the remaining will aligned to the left with no more than between each. + /// The last item will be aligned to the right and the remaining will aligned to the left. + /// Set to to ensure at least one space between + /// each item. /// /// - /// - /// 111 2222 33333 - /// + /// + /// 111 2222 33333 + /// /// - OneRightRestLeft + LastRightRestLeft } /// @@ -77,25 +85,34 @@ public enum Justification /// public class Justifier { - /// - /// Gets or sets the maximum space between items. The default is 0. For text, this is usually 1. - /// - public int MaxSpaceBetweenItems { get; set; } = 0; + private int _maxSpaceBetweenItems; /// - /// Justifies the within a container wide based on the specified + /// 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. + /// + public bool PutSpaceBetweenItems + { + get => _maxSpaceBetweenItems == 1; + set => _maxSpaceBetweenItems = value ? 1 : 0; + } + + /// + /// 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 justification style. + /// The width of the container. + /// The locations of the items, from left to right. public int [] Justify (int [] sizes, Justification justification, int totalSize) { if (sizes.Length == 0) { - return new int []{}; + return new int [] { }; } + int totalItemsSize = sizes.Sum (); if (totalItemsSize > totalSize) @@ -106,8 +123,9 @@ public class Justifier 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 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 + if (totalItemsSize >= totalSize) { spaces = 0; @@ -117,7 +135,6 @@ public class Justifier spaces = totalSize - totalItemsSize; } - switch (justification) { case Justification.Left: @@ -133,10 +150,11 @@ public class Justifier if (i == 0) { positions [0] = 0; // first item position + continue; } - var 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; @@ -153,7 +171,7 @@ public class Justifier throw new ArgumentException ("The size of an item cannot be negative."); } - var spaceBefore = spaces-- > 0 ? MaxSpaceBetweenItems : 0; + int spaceBefore = spaces-- > 0 ? _maxSpaceBetweenItems : 0; positions [i] = currentPosition; currentPosition += sizes [i] + spaceBefore; @@ -165,7 +183,7 @@ public class Justifier if (sizes.Length > 1) { // remaining space to be distributed before first and after the items - int remainingSpace = Math.Max(0, totalSize - totalItemsSize - spaces); + int remainingSpace = Math.Max (0, totalSize - totalItemsSize - spaces); for (var i = 0; i < sizes.Length; i++) { @@ -181,7 +199,7 @@ public class Justifier continue; } - var 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; @@ -193,32 +211,37 @@ public class Justifier { throw new ArgumentException ("The size of an item cannot be negative."); } + positions [0] = (totalSize - sizes [0]) / 2; // single item is centered } - break; + break; case Justification.Justified: int spaceBetween = sizes.Length > 1 ? (totalSize - totalItemsSize) / (sizes.Length - 1) : 0; int remainder = sizes.Length > 1 ? (totalSize - totalItemsSize) % (sizes.Length - 1) : 0; currentPosition = 0; + for (var i = 0; i < sizes.Length; i++) { if (sizes [i] < 0) { throw new ArgumentException ("The size of an item cannot be negative."); } + positions [i] = currentPosition; int extraSpace = i < remainder ? 1 : 0; currentPosition += sizes [i] + spaceBetween + extraSpace; } + break; - /// 111 2222 33333 - case Justification.OneRightRestLeft: + // 111 2222 33333 + case Justification.LastRightRestLeft: if (sizes.Length > 1) { currentPosition = 0; + for (var i = 0; i < sizes.Length; i++) { if (sizes [i] < 0) @@ -228,12 +251,13 @@ public class Justifier if (i < sizes.Length - 1) { - var spaceBefore = spaces-- > 0 ? MaxSpaceBetweenItems : 0; + int spaceBefore = spaces-- > 0 ? _maxSpaceBetweenItems : 0; positions [i] = currentPosition; - currentPosition += sizes [i] + spaceBefore; + currentPosition += sizes [i] + spaceBefore; } } + positions [sizes.Length - 1] = totalSize - sizes [sizes.Length - 1]; } else if (sizes.Length == 1) @@ -242,18 +266,20 @@ public class Justifier { throw new ArgumentException ("The size of an item cannot be negative."); } + positions [0] = totalSize - sizes [0]; // single item is flush right } + break; - /// 111 2222 33333 - case Justification.OneLeftRestRight: + // 111 2222 33333 + case Justification.FirstLeftRestRight: if (sizes.Length > 1) { currentPosition = 0; positions [0] = currentPosition; // first item is flush left - for (var i = sizes.Length - 1 ; i >= 0; i--) + for (int i = sizes.Length - 1; i >= 0; i--) { if (sizes [i] < 0) { @@ -269,10 +295,10 @@ public class Justifier if (i < sizes.Length - 1 && i > 0) { - var spaceBefore = spaces-- > 0 ? MaxSpaceBetweenItems : 0; + int spaceBefore = spaces-- > 0 ? _maxSpaceBetweenItems : 0; positions [i] = currentPosition - sizes [i] - spaceBefore; - currentPosition -= sizes [i + 1]; + currentPosition = positions [i]; } } } @@ -282,12 +308,14 @@ public class Justifier { throw new ArgumentException ("The size of an item cannot be negative."); } + positions [0] = 0; // single item is flush left } + break; - - + default: + throw new ArgumentOutOfRangeException (nameof (justification), justification, null); } return positions; diff --git a/UnitTests/Drawing/JustifierTests.cs b/UnitTests/Drawing/JustifierTests.cs index de7ae2e2b..f9f477a87 100644 --- a/UnitTests/Drawing/JustifierTests.cs +++ b/UnitTests/Drawing/JustifierTests.cs @@ -1,20 +1,17 @@ - using System.Text; using Xunit.Abstractions; -using Xunit.Sdk; namespace Terminal.Gui.DrawingTests; public class JustifierTests (ITestOutputHelper output) { - private readonly ITestOutputHelper _output = output; public static IEnumerable JustificationEnumValues () { - foreach (var number in Enum.GetValues (typeof (Justification))) + foreach (object number in Enum.GetValues (typeof (Justification))) { - yield return new object [] { number }; + yield return new [] { number }; } } @@ -23,7 +20,7 @@ public class JustifierTests (ITestOutputHelper output) public void NoItems_Works (Justification justification) { int [] sizes = { }; - var positions = new Justifier ().Justify (sizes, justification, 100); + int [] positions = new Justifier ().Justify (sizes, justification, 100); Assert.Equal (new int [] { }, positions); } @@ -39,308 +36,314 @@ public class JustifierTests (ITestOutputHelper output) [MemberData (nameof (JustificationEnumValues))] public void Negative_Widths_Not_Allowed (Justification justification) { - Assert.Throws (() => new Justifier ().Justify (new int [] { -10, 20, 30 }, justification, 100)); - Assert.Throws (() => new Justifier ().Justify (new int [] { 10, -20, 30 }, justification, 100)); - Assert.Throws (() => new Justifier ().Justify (new int [] { 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 ().Justify (new [] { 10, 20, -30 }, justification, 100)); } [Theory] - [InlineData (Justification.Left, new int [] { 0 }, 1, new int [] { 0 })] - [InlineData (Justification.Left, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] - [InlineData (Justification.Left, new int [] { 0, 0, 0 }, 1, new int [] { 0, 1, 1 })] - [InlineData (Justification.Left, new int [] { 1 }, 1, new int [] { 0 })] - [InlineData (Justification.Left, new int [] { 1 }, 2, new int [] { 0 })] - [InlineData (Justification.Left, new int [] { 1 }, 3, new int [] { 0 })] - [InlineData (Justification.Left, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] - [InlineData (Justification.Left, new int [] { 1, 1 }, 3, new int [] { 0, 2 })] - [InlineData (Justification.Left, new int [] { 1, 1 }, 4, new int [] { 0, 2 })] - [InlineData (Justification.Left, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] - [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] - [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 7, new int [] { 0, 2, 4 })] - [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 10, new int [] { 0, 2, 5 })] - [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 11, new int [] { 0, 2, 5 })] - [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 12, new int [] { 0, 2, 5 })] - [InlineData (Justification.Left, new int [] { 1, 2, 3 }, 13, new int [] { 0, 2, 5 })] - [InlineData (Justification.Left, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] - [InlineData (Justification.Left, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 2, 4, 7 })] - [InlineData (Justification.Left, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] - [InlineData (Justification.Left, new int [] { 10 }, 101, new int [] { 0 })] - [InlineData (Justification.Left, new int [] { 10, 20 }, 101, new int [] { 0, 11 })] - [InlineData (Justification.Left, new int [] { 10, 20, 30 }, 100, new int [] { 0, 11, 32 })] - [InlineData (Justification.Left, new int [] { 10, 20, 30 }, 101, new int [] { 0, 11, 32 })] - [InlineData (Justification.Left, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] - [InlineData (Justification.Left, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] - - [InlineData (Justification.Right, new int [] { 0 }, 1, new int [] { 1 })] - [InlineData (Justification.Right, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] - [InlineData (Justification.Right, new int [] { 0, 0, 0 }, 1, new int [] { 0, 1, 1 })] - [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] - [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 7, new int [] { 0, 2, 4 })] - [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 10, new int [] { 2, 4, 7 })] - [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 11, new int [] { 3, 5, 8 })] - [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 12, new int [] { 4, 6, 9 })] - [InlineData (Justification.Right, new int [] { 1, 2, 3 }, 13, new int [] { 5, 7, 10 })] - [InlineData (Justification.Right, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] - [InlineData (Justification.Right, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 2, 4, 7 })] - [InlineData (Justification.Right, new int [] { 10, 20, 30 }, 100, new int [] { 38, 49, 70 })] - [InlineData (Justification.Right, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] - [InlineData (Justification.Right, new int [] { 10 }, 101, new int [] { 91 })] - [InlineData (Justification.Right, new int [] { 10, 20 }, 101, new int [] { 70, 81 })] - [InlineData (Justification.Right, new int [] { 10, 20, 30 }, 101, new int [] { 39, 50, 71 })] - [InlineData (Justification.Right, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] - [InlineData (Justification.Right, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] - - [InlineData (Justification.Centered, new int [] { 0 }, 1, new int [] { 0 })] - [InlineData (Justification.Centered, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] - [InlineData (Justification.Centered, new int [] { 0, 0, 0 }, 1, new int [] { 0, 1, 1 })] - [InlineData (Justification.Centered, new int [] { 1 }, 1, new int [] { 0 })] - [InlineData (Justification.Centered, new int [] { 1 }, 2, new int [] { 0 })] - [InlineData (Justification.Centered, new int [] { 1 }, 3, new int [] { 1 })] - [InlineData (Justification.Centered, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] - [InlineData (Justification.Centered, new int [] { 1, 1 }, 3, new int [] { 0, 2 })] - [InlineData (Justification.Centered, new int [] { 1, 1 }, 4, new int [] { 0, 2 })] - [InlineData (Justification.Centered, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] - [InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] - [InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 7, new int [] { 0, 2, 4 })] - [InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 10, new int [] { 1, 3, 6 })] - [InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 11, new int [] { 1, 3, 6 })] - [InlineData (Justification.Centered, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] - [InlineData (Justification.Centered, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 2, 4, 7 })] - [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 9, new int [] { 0, 3, 6 })] - [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 10, new int [] { 0, 4, 7 })] - [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 11, new int [] { 0, 4, 8 })] - [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 12, new int [] { 0, 4, 8 })] - [InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 13, new int [] { 1, 5, 9 })] - [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] - [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 101, new int [] { 0, 34, 68 })] - [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 102, new int [] { 0, 34, 68 })] - [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 103, new int [] { 1, 35, 69 })] - [InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 104, new int [] { 1, 35, 69 })] - [InlineData (Justification.Centered, new int [] { 10 }, 101, new int [] { 45 })] - [InlineData (Justification.Centered, new int [] { 10, 20 }, 101, new int [] { 35, 46 })] - [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 100, new int [] { 19, 30, 51 })] - [InlineData (Justification.Centered, new int [] { 10, 20, 30 }, 101, new int [] { 19, 30, 51 })] - [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40 }, 100, new int [] { 0, 10, 30, 60 })] - [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] - [InlineData (Justification.Centered, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] - [InlineData (Justification.Centered, new int [] { 3, 4, 5, 6 }, 25, new int [] { 2, 6, 11, 17 })] - - [InlineData (Justification.Justified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] - [InlineData (Justification.Justified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] - [InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 30, 70 })] - [InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 31, 71 })] - [InlineData (Justification.Justified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] - [InlineData (Justification.Justified, new int [] { 11, 17, 23 }, 100, new int [] { 0, 36, 77 })] - [InlineData (Justification.Justified, new int [] { 1, 2, 3 }, 11, new int [] { 0, 4, 8 })] - [InlineData (Justification.Justified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - [InlineData (Justification.Justified, new int [] { 10 }, 101, new int [] { 0 })] - [InlineData (Justification.Justified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 9, 18 })] - [InlineData (Justification.Justified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 8, 16 })] - [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 18, new int [] { 0, 3, 7, 12 })] - [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 19, new int [] { 0, 4, 8, 13 })] - [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 20, new int [] { 0, 4, 9, 14 })] - [InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 21, new int [] { 0, 4, 9, 15 })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 22, new int [] { 0, 8, 14, 19 })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 23, new int [] { 0, 8, 15, 20, })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 24, new int [] { 0, 8, 15, 21 })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 25, new int [] { 0, 9, 16, 22 })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 26, new int [] { 0, 9, 17, 23 })] - [InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 31, new int [] { 0, 11, 20, 28 })] - - [InlineData (Justification.OneRightRestLeft, new int [] { 0 }, 1, new int [] { 0 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 0, 0, 0 }, 1, new int [] { 0, 1, 1 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 1, new int [] { 0 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 2, new int [] { 0 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 3, new int [] { 0 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 3, new int [] { 0, 2 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 4, new int [] { 0, 3 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 7, new int [] { 0, 2, 4 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 8, new int [] { 0, 2, 5 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 9, new int [] { 0, 2, 6 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 10, new int [] { 0, 2, 7 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 11, new int [] { 0, 2, 8 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 2, 4, 7 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 3, 3, 3 }, 21, new int [] { 0, 4, 18 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 3, 4, 5 }, 21, new int [] { 0, 4, 16 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 10 }, 101, new int [] { 0 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30 }, 100, new int [] { 0, 11, 70 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30 }, 101, new int [] { 0, 11, 71 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] - [InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] - - //[InlineData (Justification.SplitLeft, new int [] { 10, 20, 30 }, 100, new int [] { 0, 49, 70 })] - //[InlineData (Justification.SplitLeft, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 67 })] - //[InlineData (Justification.SplitLeft, new int [] { 10 }, 101, new int [] { 0 })] - //[InlineData (Justification.SplitLeft, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - //[InlineData (Justification.SplitLeft, new int [] { 10, 20, 30 }, 101, new int [] { 0, 50, 71 })] - //[InlineData (Justification.SplitLeft, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 9, 30, 61 })] - //[InlineData (Justification.SplitLeft, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 8, 29, 60, 101 })] - //[InlineData (Justification.SplitLeft, new int [] { 3, 3, 3 }, 21, new int [] { 0, 14, 18 })] - //[InlineData (Justification.SplitLeft, new int [] { 3, 4, 5 }, 21, new int [] { 0, 11, 16 })] - - public void TestJustifications_1Space (Justification justification, int [] sizes, int totalSize, int [] expected) + [InlineData (Justification.Left, new [] { 0 }, 1, new [] { 0 })] + [InlineData (Justification.Left, new [] { 0, 0 }, 1, new [] { 0, 1 })] + [InlineData (Justification.Left, new [] { 0, 0, 0 }, 1, new [] { 0, 1, 1 })] + [InlineData (Justification.Left, new [] { 1 }, 1, new [] { 0 })] + [InlineData (Justification.Left, new [] { 1 }, 2, new [] { 0 })] + [InlineData (Justification.Left, new [] { 1 }, 3, new [] { 0 })] + [InlineData (Justification.Left, new [] { 1, 1 }, 2, new [] { 0, 1 })] + [InlineData (Justification.Left, new [] { 1, 1 }, 3, new [] { 0, 2 })] + [InlineData (Justification.Left, new [] { 1, 1 }, 4, new [] { 0, 2 })] + [InlineData (Justification.Left, new [] { 1, 1, 1 }, 3, new [] { 0, 1, 2 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 6, new [] { 0, 1, 3 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 7, new [] { 0, 2, 4 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 10, new [] { 0, 2, 5 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 11, new [] { 0, 2, 5 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 12, new [] { 0, 2, 5 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 13, new [] { 0, 2, 5 })] + [InlineData (Justification.Left, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.Left, new [] { 1, 2, 3, 4 }, 11, new [] { 0, 2, 4, 7 })] + [InlineData (Justification.Left, new [] { 33, 33, 33 }, 100, new [] { 0, 34, 67 })] + [InlineData (Justification.Left, new [] { 10 }, 101, new [] { 0 })] + [InlineData (Justification.Left, new [] { 10, 20 }, 101, new [] { 0, 11 })] + [InlineData (Justification.Left, new [] { 10, 20, 30 }, 100, new [] { 0, 11, 32 })] + [InlineData (Justification.Left, new [] { 10, 20, 30 }, 101, new [] { 0, 11, 32 })] + [InlineData (Justification.Left, new [] { 10, 20, 30, 40 }, 101, new [] { 0, 11, 31, 61 })] + [InlineData (Justification.Left, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 0, 11, 31, 61, 101 })] + [InlineData (Justification.Right, new [] { 0 }, 1, new [] { 1 })] + [InlineData (Justification.Right, new [] { 0, 0 }, 1, new [] { 0, 1 })] + [InlineData (Justification.Right, new [] { 0, 0, 0 }, 1, new [] { 0, 1, 1 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 6, new [] { 0, 1, 3 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 7, new [] { 0, 2, 4 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 10, new [] { 2, 4, 7 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 11, new [] { 3, 5, 8 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 12, new [] { 4, 6, 9 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 13, new [] { 5, 7, 10 })] + [InlineData (Justification.Right, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.Right, new [] { 1, 2, 3, 4 }, 11, new [] { 0, 2, 4, 7 })] + [InlineData (Justification.Right, new [] { 10, 20, 30 }, 100, new [] { 38, 49, 70 })] + [InlineData (Justification.Right, new [] { 33, 33, 33 }, 100, new [] { 0, 34, 67 })] + [InlineData (Justification.Right, new [] { 10 }, 101, new [] { 91 })] + [InlineData (Justification.Right, new [] { 10, 20 }, 101, new [] { 70, 81 })] + [InlineData (Justification.Right, new [] { 10, 20, 30 }, 101, new [] { 39, 50, 71 })] + [InlineData (Justification.Right, new [] { 10, 20, 30, 40 }, 101, new [] { 0, 11, 31, 61 })] + [InlineData (Justification.Right, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 0, 11, 31, 61, 101 })] + [InlineData (Justification.Centered, new [] { 0 }, 1, new [] { 0 })] + [InlineData (Justification.Centered, new [] { 0, 0 }, 1, new [] { 0, 1 })] + [InlineData (Justification.Centered, new [] { 0, 0, 0 }, 1, new [] { 0, 1, 1 })] + [InlineData (Justification.Centered, new [] { 1 }, 1, new [] { 0 })] + [InlineData (Justification.Centered, new [] { 1 }, 2, new [] { 0 })] + [InlineData (Justification.Centered, new [] { 1 }, 3, new [] { 1 })] + [InlineData (Justification.Centered, new [] { 1, 1 }, 2, new [] { 0, 1 })] + [InlineData (Justification.Centered, new [] { 1, 1 }, 3, new [] { 0, 2 })] + [InlineData (Justification.Centered, new [] { 1, 1 }, 4, new [] { 0, 2 })] + [InlineData (Justification.Centered, new [] { 1, 1, 1 }, 3, new [] { 0, 1, 2 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3 }, 6, new [] { 0, 1, 3 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3 }, 7, new [] { 0, 2, 4 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3 }, 10, new [] { 1, 3, 6 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3 }, 11, new [] { 1, 3, 6 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3, 4 }, 11, new [] { 0, 2, 4, 7 })] + [InlineData (Justification.Centered, new [] { 3, 3, 3 }, 9, new [] { 0, 3, 6 })] + [InlineData (Justification.Centered, new [] { 3, 3, 3 }, 10, new [] { 0, 4, 7 })] + [InlineData (Justification.Centered, new [] { 3, 3, 3 }, 11, new [] { 0, 4, 8 })] + [InlineData (Justification.Centered, new [] { 3, 3, 3 }, 12, new [] { 0, 4, 8 })] + [InlineData (Justification.Centered, new [] { 3, 3, 3 }, 13, new [] { 1, 5, 9 })] + [InlineData (Justification.Centered, new [] { 33, 33, 33 }, 100, new [] { 0, 34, 67 })] + [InlineData (Justification.Centered, new [] { 33, 33, 33 }, 101, new [] { 0, 34, 68 })] + [InlineData (Justification.Centered, new [] { 33, 33, 33 }, 102, new [] { 0, 34, 68 })] + [InlineData (Justification.Centered, new [] { 33, 33, 33 }, 103, new [] { 1, 35, 69 })] + [InlineData (Justification.Centered, new [] { 33, 33, 33 }, 104, new [] { 1, 35, 69 })] + [InlineData (Justification.Centered, new [] { 10 }, 101, new [] { 45 })] + [InlineData (Justification.Centered, new [] { 10, 20 }, 101, new [] { 35, 46 })] + [InlineData (Justification.Centered, new [] { 10, 20, 30 }, 100, new [] { 19, 30, 51 })] + [InlineData (Justification.Centered, new [] { 10, 20, 30 }, 101, new [] { 19, 30, 51 })] + [InlineData (Justification.Centered, new [] { 10, 20, 30, 40 }, 100, new [] { 0, 10, 30, 60 })] + [InlineData (Justification.Centered, new [] { 10, 20, 30, 40 }, 101, new [] { 0, 11, 31, 61 })] + [InlineData (Justification.Centered, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 0, 11, 31, 61, 101 })] + [InlineData (Justification.Centered, new [] { 3, 4, 5, 6 }, 25, new [] { 2, 6, 11, 17 })] + [InlineData (Justification.Justified, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 0, 11, 31, 61, 101 })] + [InlineData (Justification.Justified, new [] { 10, 20, 30, 40 }, 101, new [] { 0, 11, 31, 61 })] + [InlineData (Justification.Justified, new [] { 10, 20, 30 }, 100, new [] { 0, 30, 70 })] + [InlineData (Justification.Justified, new [] { 10, 20, 30 }, 101, new [] { 0, 31, 71 })] + [InlineData (Justification.Justified, new [] { 33, 33, 33 }, 100, new [] { 0, 34, 67 })] + [InlineData (Justification.Justified, new [] { 11, 17, 23 }, 100, new [] { 0, 36, 77 })] + [InlineData (Justification.Justified, new [] { 1, 2, 3 }, 11, new [] { 0, 4, 8 })] + [InlineData (Justification.Justified, new [] { 10, 20 }, 101, new [] { 0, 81 })] + [InlineData (Justification.Justified, new [] { 10 }, 101, new [] { 0 })] + [InlineData (Justification.Justified, new [] { 3, 3, 3 }, 21, new [] { 0, 9, 18 })] + [InlineData (Justification.Justified, new [] { 3, 4, 5 }, 21, new [] { 0, 8, 16 })] + [InlineData (Justification.Justified, new [] { 3, 4, 5, 6 }, 18, new [] { 0, 3, 7, 12 })] + [InlineData (Justification.Justified, new [] { 3, 4, 5, 6 }, 19, new [] { 0, 4, 8, 13 })] + [InlineData (Justification.Justified, new [] { 3, 4, 5, 6 }, 20, new [] { 0, 4, 9, 14 })] + [InlineData (Justification.Justified, new [] { 3, 4, 5, 6 }, 21, new [] { 0, 4, 9, 15 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 22, new [] { 0, 8, 14, 19 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 23, new [] { 0, 8, 15, 20 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 24, new [] { 0, 8, 15, 21 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 25, new [] { 0, 9, 16, 22 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 26, new [] { 0, 9, 17, 23 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 31, new [] { 0, 11, 20, 28 })] + [InlineData (Justification.LastRightRestLeft, new [] { 0 }, 1, new [] { 1 })] + [InlineData (Justification.LastRightRestLeft, new [] { 0, 0 }, 1, new [] { 0, 1 })] + [InlineData (Justification.LastRightRestLeft, new [] { 0, 0, 0 }, 1, new [] { 0, 1, 1 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1 }, 1, new [] { 0 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1 }, 2, new [] { 1 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1 }, 3, new [] { 2 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 1 }, 2, new [] { 0, 1 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 1 }, 3, new [] { 0, 2 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 1 }, 4, new [] { 0, 3 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 1, 1 }, 3, new [] { 0, 1, 2 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 6, new [] { 0, 1, 3 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 7, new [] { 0, 2, 4 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 8, new [] { 0, 2, 5 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 9, new [] { 0, 2, 6 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 10, new [] { 0, 2, 7 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 11, new [] { 0, 2, 8 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3, 4 }, 11, new [] { 0, 2, 4, 7 })] + [InlineData (Justification.LastRightRestLeft, new [] { 3, 3, 3 }, 21, new [] { 0, 4, 18 })] + [InlineData (Justification.LastRightRestLeft, new [] { 3, 4, 5 }, 21, new [] { 0, 4, 16 })] + [InlineData (Justification.LastRightRestLeft, new [] { 33, 33, 33 }, 100, new [] { 0, 34, 67 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10 }, 101, new [] { 91 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10, 20 }, 101, new [] { 0, 81 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10, 20, 30 }, 100, new [] { 0, 11, 70 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10, 20, 30 }, 101, new [] { 0, 11, 71 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10, 20, 30, 40 }, 101, new [] { 0, 11, 31, 61 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 0, 11, 31, 61, 101 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 0 }, 1, new [] { 0 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 0, 0 }, 1, new [] { 0, 1 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 0, 0, 0 }, 1, new [] { 0, 0, 1 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1 }, 1, new [] { 0 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1 }, 2, new [] { 0 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1 }, 3, new [] { 0 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 1 }, 2, new [] { 0, 1 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 1 }, 3, new [] { 0, 2 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 1 }, 4, new [] { 0, 3 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 1, 1 }, 3, new [] { 0, 1, 2 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 6, new [] { 0, 1, 3 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 7, new [] { 0, 1, 4 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 8, new [] { 0, 2, 5 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 9, new [] { 0, 3, 6 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 10, new [] { 0, 4, 7 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 11, new [] { 0, 5, 8 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3, 4 }, 11, new [] { 0, 1, 3, 7 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3, 4 }, 12, new [] { 0, 1, 4, 8 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 3, 3, 3 }, 21, new [] { 0, 14, 18 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 3, 4, 5 }, 21, new [] { 0, 11, 16 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 33, 33, 33 }, 100, new [] { 0, 33, 67 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 10 }, 101, new [] { 0 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 10, 20 }, 101, new [] { 0, 81 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 10, 20, 30 }, 100, new [] { 0, 49, 70 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 10, 20, 30 }, 101, new [] { 0, 50, 71 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 10, 20, 30, 40 }, 101, new [] { 0, 10, 30, 61 })] + [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) { - var positions = new Justifier () { MaxSpaceBetweenItems = 1 }.Justify (sizes, justification, totalSize); + int [] positions = new Justifier { PutSpaceBetweenItems = true }.Justify (sizes, justification, totalSize); AssertJustification (justification, sizes, totalSize, positions, expected); } [Theory] - //[InlineData (Justification.Left, new int [] { 0 }, 1, new int [] { 0 })] - //[InlineData (Justification.Left, new int [] { 0, 0 }, 1, new int [] { 0, 0 })] - //[InlineData (Justification.Left, new int [] { 0, 0, 0 }, 1, new int [] { 0, 0, 0 })] - //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] - //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 7, new int [] { 0, 1, 3 })] - //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 10, new int [] { 0, 1, 3 })] - //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 11, new int [] { 0, 1, 3 })] - //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 12, new int [] { 0, 1, 3 })] - //[InlineData (Justification.Left, new int [] { 1, 2, 3 }, 13, new int [] { 0, 1, 3 })] - //[InlineData (Justification.Left, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] - //[InlineData (Justification.Left, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 1, 3, 6 })] - //[InlineData (Justification.Left, new int [] { 10, 20, 30 }, 100, new int [] { 0, 10, 30 })] - //[InlineData (Justification.Left, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 66 })] - //[InlineData (Justification.Left, new int [] { 10 }, 101, new int [] { 0 })] - //[InlineData (Justification.Left, new int [] { 10, 20 }, 101, new int [] { 0, 10 })] - //[InlineData (Justification.Left, new int [] { 10, 20, 30 }, 101, new int [] { 0, 10, 30 })] - //[InlineData (Justification.Left, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 60 })] - //[InlineData (Justification.Left, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 100 })] - - //[InlineData (Justification.Right, new int [] { 0 }, 1, new int [] { 1 })] - //[InlineData (Justification.Right, new int [] { 0, 0 }, 1, new int [] { 1, 1 })] - //[InlineData (Justification.Right, new int [] { 0, 0, 0 }, 1, new int [] { 1, 1, 1 })] - //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] - //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 7, new int [] { 1, 2, 4 })] - //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 10, new int [] { 4, 5, 7 })] - //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 11, new int [] { 5, 6, 8 })] - //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 12, new int [] { 6, 7, 9 })] - //[InlineData (Justification.Right, new int [] { 1, 2, 3 }, 13, new int [] { 7, 8, 10 })] - //[InlineData (Justification.Right, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] - //[InlineData (Justification.Right, new int [] { 1, 2, 3, 4 }, 11, new int [] { 1, 2, 4, 7 })] - //[InlineData (Justification.Right, new int [] { 10, 20, 30 }, 100, new int [] { 40, 50, 70 })] - //[InlineData (Justification.Right, new int [] { 33, 33, 33 }, 100, new int [] { 1, 34, 67 })] - //[InlineData (Justification.Right, new int [] { 10 }, 101, new int [] { 91 })] - //[InlineData (Justification.Right, new int [] { 10, 20 }, 101, new int [] { 71, 81 })] - //[InlineData (Justification.Right, new int [] { 10, 20, 30 }, 101, new int [] { 41, 51, 71 })] - //[InlineData (Justification.Right, new int [] { 10, 20, 30, 40 }, 101, new int [] { 1, 11, 31, 61 })] - //[InlineData (Justification.Right, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 1, 11, 31, 61, 101 })] - - //[InlineData (Justification.Centered, new int [] { 1 }, 1, new int [] { 0 })] - //[InlineData (Justification.Centered, new int [] { 1 }, 2, new int [] { 0 })] - //[InlineData (Justification.Centered, new int [] { 1 }, 3, new int [] { 1 })] - //[InlineData (Justification.Centered, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] - //[InlineData (Justification.Centered, new int [] { 1, 1 }, 3, new int [] { 0, 1 })] - //[InlineData (Justification.Centered, new int [] { 1, 1 }, 4, new int [] { 1, 2 })] - //[InlineData (Justification.Centered, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] - //[InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] - //[InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 7, new int [] { 0, 1, 3 })] - //[InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 10, new int [] { 2, 3, 5 })] - //[InlineData (Justification.Centered, new int [] { 1, 2, 3 }, 11, new int [] { 2, 3, 5 })] - //[InlineData (Justification.Centered, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] - //[InlineData (Justification.Centered, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 1, 3, 6 })] - //[InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 9, new int [] { 0, 3, 6 })] - //[InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 10, new int [] { 0, 3, 6 })] - //[InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 11, new int [] { 1, 4, 7 })] - //[InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 12, new int [] { 1, 4, 7 })] - //[InlineData (Justification.Centered, new int [] { 3, 3, 3 }, 13, new int [] { 2, 5, 8 })] - //[InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 66 })] - //[InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 101, new int [] { 1, 34, 67 })] - //[InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 102, new int [] { 1, 34, 67 })] - //[InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 103, new int [] { 2, 35, 68 })] - //[InlineData (Justification.Centered, new int [] { 33, 33, 33 }, 104, new int [] { 2, 35, 68 })] - //[InlineData (Justification.Centered, new int [] { 3, 4, 5, 6 }, 25, new int [] { 3, 6, 10, 15 })] - - //[InlineData (Justification.Justified, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 11, 31, 61, 101 })] - //[InlineData (Justification.Justified, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 11, 31, 61 })] - //[InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 100, new int [] { 0, 30, 70 })] - //[InlineData (Justification.Justified, new int [] { 10, 20, 30 }, 101, new int [] { 0, 31, 71 })] - //[InlineData (Justification.Justified, new int [] { 33, 33, 33 }, 100, new int [] { 0, 34, 67 })] - //[InlineData (Justification.Justified, new int [] { 11, 17, 23 }, 100, new int [] { 0, 36, 77 })] - //[InlineData (Justification.Justified, new int [] { 1, 2, 3 }, 11, new int [] { 0, 4, 8 })] - //[InlineData (Justification.Justified, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - //[InlineData (Justification.Justified, new int [] { 10 }, 101, new int [] { 0 })] - //[InlineData (Justification.Justified, new int [] { 3, 3, 3 }, 21, new int [] { 0, 9, 18 })] - //[InlineData (Justification.Justified, new int [] { 3, 4, 5 }, 21, new int [] { 0, 8, 16 })] - //[InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 18, new int [] { 0, 3, 7, 12 })] - //[InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 19, new int [] { 0, 4, 8, 13 })] - //[InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 20, new int [] { 0, 4, 9, 14 })] - //[InlineData (Justification.Justified, new int [] { 3, 4, 5, 6 }, 21, new int [] { 0, 4, 9, 15 })] - //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 22, new int [] { 0, 8, 14, 19 })] - //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 23, new int [] { 0, 8, 15, 20, })] - //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 24, new int [] { 0, 8, 15, 21 })] - //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 25, new int [] { 0, 9, 16, 22 })] - //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 26, new int [] { 0, 9, 17, 23 })] - //[InlineData (Justification.Justified, new int [] { 6, 5, 4, 3 }, 31, new int [] { 0, 11, 20, 28 })] - - //[InlineData (Justification.OneRightRestLeft, new int [] { 0 }, 1, new int [] { 0 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 0, 0, 0 }, 1, new int [] { 0, 0, 1 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 1, new int [] { 0 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 2, new int [] { 0 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1 }, 3, new int [] { 0 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 3, new int [] { 0, 2 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 1 }, 4, new int [] { 0, 3 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 7, new int [] { 0, 1, 4 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 8, new int [] { 0, 1, 5 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 9, new int [] { 0, 1, 6 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 10, new int [] { 0, 1, 7 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3 }, 11, new int [] { 0, 1, 8 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 1, 3, 7 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 1, 2, 3, 4 }, 12, new int [] { 0, 1, 3, 8 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 3, 3, 3 }, 21, new int [] { 0, 3, 18 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 3, 4, 5 }, 21, new int [] { 0, 3, 16 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 67 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 10 }, 101, new int [] { 0 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30 }, 100, new int [] { 0, 10, 70 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30 }, 101, new int [] { 0, 10, 71 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 61 })] - //[InlineData (Justification.OneRightRestLeft, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 101, })] - - - [InlineData (Justification.OneLeftRestRight, new int [] { 0 }, 1, new int [] { 0 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 0, 0 }, 1, new int [] { 0, 1 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 0, 0, 0 }, 1, new int [] { 0, 1, 1 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1 }, 1, new int [] { 0 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1 }, 2, new int [] { 1 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1 }, 3, new int [] { 2 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1, 1 }, 2, new int [] { 0, 1 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1, 1 }, 3, new int [] { 0, 2 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1, 1 }, 4, new int [] { 0, 3 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1, 1, 1 }, 3, new int [] { 0, 1, 2 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 6, new int [] { 0, 1, 3 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 7, new int [] { 0, 2, 4 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 8, new int [] { 0, 3, 5 })] - [InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 9, new int [] { 0, 4, 6 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 10, new int [] { 0, 1, 7 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3 }, 11, new int [] { 0, 1, 8 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3, 4 }, 10, new int [] { 0, 1, 3, 6 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3, 4 }, 11, new int [] { 0, 1, 3, 7 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 1, 2, 3, 4 }, 12, new int [] { 0, 1, 3, 8 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 3, 3, 3 }, 21, new int [] { 0, 3, 18 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 3, 4, 5 }, 21, new int [] { 0, 3, 16 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 33, 33, 33 }, 100, new int [] { 0, 33, 67 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 10 }, 101, new int [] { 0 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 10, 20 }, 101, new int [] { 0, 81 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 10, 20, 30 }, 100, new int [] { 0, 10, 70 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 10, 20, 30 }, 101, new int [] { 0, 10, 71 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 10, 20, 30, 40 }, 101, new int [] { 0, 10, 30, 61 })] - //[InlineData (Justification.OneLeftRestRight, new int [] { 10, 20, 30, 40, 50 }, 151, new int [] { 0, 10, 30, 60, 101, })] - - - - public void TestJustifications_0Space (Justification justification, int [] sizes, int totalSize, int [] expected) + [InlineData (Justification.Left, new [] { 0 }, 1, new [] { 0 })] + [InlineData (Justification.Left, new [] { 0, 0 }, 1, new [] { 0, 0 })] + [InlineData (Justification.Left, new [] { 0, 0, 0 }, 1, new [] { 0, 0, 0 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 6, new [] { 0, 1, 3 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 7, new [] { 0, 1, 3 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 10, new [] { 0, 1, 3 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 11, new [] { 0, 1, 3 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 12, new [] { 0, 1, 3 })] + [InlineData (Justification.Left, new [] { 1, 2, 3 }, 13, new [] { 0, 1, 3 })] + [InlineData (Justification.Left, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.Left, new [] { 1, 2, 3, 4 }, 11, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.Left, new [] { 10, 20, 30 }, 100, new [] { 0, 10, 30 })] + [InlineData (Justification.Left, new [] { 33, 33, 33 }, 100, new [] { 0, 33, 66 })] + [InlineData (Justification.Left, new [] { 10 }, 101, new [] { 0 })] + [InlineData (Justification.Left, new [] { 10, 20 }, 101, new [] { 0, 10 })] + [InlineData (Justification.Left, new [] { 10, 20, 30 }, 101, new [] { 0, 10, 30 })] + [InlineData (Justification.Left, new [] { 10, 20, 30, 40 }, 101, new [] { 0, 10, 30, 60 })] + [InlineData (Justification.Left, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 0, 10, 30, 60, 100 })] + [InlineData (Justification.Right, new [] { 0 }, 1, new [] { 1 })] + [InlineData (Justification.Right, new [] { 0, 0 }, 1, new [] { 1, 1 })] + [InlineData (Justification.Right, new [] { 0, 0, 0 }, 1, new [] { 1, 1, 1 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 6, new [] { 0, 1, 3 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 7, new [] { 1, 2, 4 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 10, new [] { 4, 5, 7 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 11, new [] { 5, 6, 8 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 12, new [] { 6, 7, 9 })] + [InlineData (Justification.Right, new [] { 1, 2, 3 }, 13, new [] { 7, 8, 10 })] + [InlineData (Justification.Right, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.Right, new [] { 1, 2, 3, 4 }, 11, new [] { 1, 2, 4, 7 })] + [InlineData (Justification.Right, new [] { 10, 20, 30 }, 100, new [] { 40, 50, 70 })] + [InlineData (Justification.Right, new [] { 33, 33, 33 }, 100, new [] { 1, 34, 67 })] + [InlineData (Justification.Right, new [] { 10 }, 101, new [] { 91 })] + [InlineData (Justification.Right, new [] { 10, 20 }, 101, new [] { 71, 81 })] + [InlineData (Justification.Right, new [] { 10, 20, 30 }, 101, new [] { 41, 51, 71 })] + [InlineData (Justification.Right, new [] { 10, 20, 30, 40 }, 101, new [] { 1, 11, 31, 61 })] + [InlineData (Justification.Right, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 1, 11, 31, 61, 101 })] + [InlineData (Justification.Centered, new [] { 1 }, 1, new [] { 0 })] + [InlineData (Justification.Centered, new [] { 1 }, 2, new [] { 0 })] + [InlineData (Justification.Centered, new [] { 1 }, 3, new [] { 1 })] + [InlineData (Justification.Centered, new [] { 1, 1 }, 2, new [] { 0, 1 })] + [InlineData (Justification.Centered, new [] { 1, 1 }, 3, new [] { 0, 1 })] + [InlineData (Justification.Centered, new [] { 1, 1 }, 4, new [] { 1, 2 })] + [InlineData (Justification.Centered, new [] { 1, 1, 1 }, 3, new [] { 0, 1, 2 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3 }, 6, new [] { 0, 1, 3 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3 }, 7, new [] { 0, 1, 3 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3 }, 10, new [] { 2, 3, 5 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3 }, 11, new [] { 2, 3, 5 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.Centered, new [] { 1, 2, 3, 4 }, 11, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.Centered, new [] { 3, 3, 3 }, 9, new [] { 0, 3, 6 })] + [InlineData (Justification.Centered, new [] { 3, 3, 3 }, 10, new [] { 0, 3, 6 })] + [InlineData (Justification.Centered, new [] { 3, 3, 3 }, 11, new [] { 1, 4, 7 })] + [InlineData (Justification.Centered, new [] { 3, 3, 3 }, 12, new [] { 1, 4, 7 })] + [InlineData (Justification.Centered, new [] { 3, 3, 3 }, 13, new [] { 2, 5, 8 })] + [InlineData (Justification.Centered, new [] { 33, 33, 33 }, 100, new [] { 0, 33, 66 })] + [InlineData (Justification.Centered, new [] { 33, 33, 33 }, 101, new [] { 1, 34, 67 })] + [InlineData (Justification.Centered, new [] { 33, 33, 33 }, 102, new [] { 1, 34, 67 })] + [InlineData (Justification.Centered, new [] { 33, 33, 33 }, 103, new [] { 2, 35, 68 })] + [InlineData (Justification.Centered, new [] { 33, 33, 33 }, 104, new [] { 2, 35, 68 })] + [InlineData (Justification.Centered, new [] { 3, 4, 5, 6 }, 25, new [] { 3, 6, 10, 15 })] + [InlineData (Justification.Justified, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 0, 11, 31, 61, 101 })] + [InlineData (Justification.Justified, new [] { 10, 20, 30, 40 }, 101, new [] { 0, 11, 31, 61 })] + [InlineData (Justification.Justified, new [] { 10, 20, 30 }, 100, new [] { 0, 30, 70 })] + [InlineData (Justification.Justified, new [] { 10, 20, 30 }, 101, new [] { 0, 31, 71 })] + [InlineData (Justification.Justified, new [] { 33, 33, 33 }, 100, new [] { 0, 34, 67 })] + [InlineData (Justification.Justified, new [] { 11, 17, 23 }, 100, new [] { 0, 36, 77 })] + [InlineData (Justification.Justified, new [] { 1, 2, 3 }, 11, new [] { 0, 4, 8 })] + [InlineData (Justification.Justified, new [] { 10, 20 }, 101, new [] { 0, 81 })] + [InlineData (Justification.Justified, new [] { 10 }, 101, new [] { 0 })] + [InlineData (Justification.Justified, new [] { 3, 3, 3 }, 21, new [] { 0, 9, 18 })] + [InlineData (Justification.Justified, new [] { 3, 4, 5 }, 21, new [] { 0, 8, 16 })] + [InlineData (Justification.Justified, new [] { 3, 4, 5, 6 }, 18, new [] { 0, 3, 7, 12 })] + [InlineData (Justification.Justified, new [] { 3, 4, 5, 6 }, 19, new [] { 0, 4, 8, 13 })] + [InlineData (Justification.Justified, new [] { 3, 4, 5, 6 }, 20, new [] { 0, 4, 9, 14 })] + [InlineData (Justification.Justified, new [] { 3, 4, 5, 6 }, 21, new [] { 0, 4, 9, 15 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 22, new [] { 0, 8, 14, 19 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 23, new [] { 0, 8, 15, 20 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 24, new [] { 0, 8, 15, 21 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 25, new [] { 0, 9, 16, 22 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 26, new [] { 0, 9, 17, 23 })] + [InlineData (Justification.Justified, new [] { 6, 5, 4, 3 }, 31, new [] { 0, 11, 20, 28 })] + [InlineData (Justification.LastRightRestLeft, new [] { 0 }, 1, new [] { 1 })] + [InlineData (Justification.LastRightRestLeft, new [] { 0, 0 }, 1, new [] { 0, 1 })] + [InlineData (Justification.LastRightRestLeft, new [] { 0, 0, 0 }, 1, new [] { 0, 0, 1 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1 }, 1, new [] { 0 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1 }, 2, new [] { 1 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1 }, 3, new [] { 2 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 1 }, 2, new [] { 0, 1 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 1 }, 3, new [] { 0, 2 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 1 }, 4, new [] { 0, 3 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 1, 1 }, 3, new [] { 0, 1, 2 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 6, new [] { 0, 1, 3 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 7, new [] { 0, 1, 4 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 8, new [] { 0, 1, 5 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 9, new [] { 0, 1, 6 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 10, new [] { 0, 1, 7 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3 }, 11, new [] { 0, 1, 8 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3, 4 }, 11, new [] { 0, 1, 3, 7 })] + [InlineData (Justification.LastRightRestLeft, new [] { 1, 2, 3, 4 }, 12, new [] { 0, 1, 3, 8 })] + [InlineData (Justification.LastRightRestLeft, new [] { 3, 3, 3 }, 21, new [] { 0, 3, 18 })] + [InlineData (Justification.LastRightRestLeft, new [] { 3, 4, 5 }, 21, new [] { 0, 3, 16 })] + [InlineData (Justification.LastRightRestLeft, new [] { 33, 33, 33 }, 100, new [] { 0, 33, 67 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10 }, 101, new [] { 91 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10, 20 }, 101, new [] { 0, 81 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10, 20, 30 }, 100, new [] { 0, 10, 70 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10, 20, 30 }, 101, new [] { 0, 10, 71 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10, 20, 30, 40 }, 101, new [] { 0, 10, 30, 61 })] + [InlineData (Justification.LastRightRestLeft, new [] { 10, 20, 30, 40, 50 }, 151, new [] { 0, 10, 30, 60, 101 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 0 }, 1, new [] { 0 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 0, 0 }, 1, new [] { 0, 1 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 0, 0, 0 }, 1, new [] { 0, 1, 1 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1 }, 1, new [] { 0 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1 }, 2, new [] { 0 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1 }, 3, new [] { 0 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 1 }, 2, new [] { 0, 1 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 1 }, 3, new [] { 0, 2 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 1 }, 4, new [] { 0, 3 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 1, 1 }, 3, new [] { 0, 1, 2 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 6, new [] { 0, 1, 3 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 7, new [] { 0, 2, 4 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 8, new [] { 0, 3, 5 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 9, new [] { 0, 4, 6 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 10, new [] { 0, 5, 7 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3 }, 11, new [] { 0, 6, 8 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3, 4 }, 10, new [] { 0, 1, 3, 6 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3, 4 }, 11, new [] { 0, 2, 4, 7 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 1, 2, 3, 4 }, 12, new [] { 0, 3, 5, 8 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 3, 3, 3 }, 21, new [] { 0, 15, 18 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 3, 4, 5 }, 21, new [] { 0, 12, 16 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 33, 33, 33 }, 100, new [] { 0, 34, 67 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 10 }, 101, new [] { 0 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 10, 20 }, 101, new [] { 0, 81 })] + [InlineData (Justification.FirstLeftRestRight, new [] { 10, 20, 30 }, 100, new [] { 0, 50, 70 })] + [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) { - var positions = new Justifier () { MaxSpaceBetweenItems = 0 }.Justify (sizes, justification, totalSize); + int [] positions = new Justifier { PutSpaceBetweenItems = false }.Justify (sizes, justification, totalSize); AssertJustification (justification, sizes, totalSize, positions, expected); } @@ -364,33 +367,37 @@ public class JustifierTests (ITestOutputHelper output) } } - public string RenderJustification (Justification justification, int [] sizes, int totalSize, int [] positions) { var output = new StringBuilder (); output.AppendLine ($"Justification: {justification}, Positions: {string.Join (", ", positions)}, TotalSize: {totalSize}"); - for (int i = 0; i <= totalSize / 10; i++) + + for (var i = 0; i <= totalSize / 10; i++) { output.Append (i.ToString ().PadRight (9) + " "); } + output.AppendLine (); - for (int i = 0; i < totalSize; i++) + for (var i = 0; i < totalSize; i++) { output.Append (i % 10); } + output.AppendLine (); var items = new char [totalSize]; - for (int position = 0; position < positions.Length; position++) + + for (var position = 0; position < positions.Length; position++) { // try { - for (int j = 0; j < sizes [position] && positions [position] + j < totalSize; j++) + for (var j = 0; j < sizes [position] && positions [position] + j < totalSize; j++) { items [positions [position] + j] = (position + 1).ToString () [0]; } } + //catch (Exception e) //{ // output.AppendLine ($"{e.Message} - position = {position}, positions[{position}]: {positions [position]}, sizes[{position}]: {sizes [position]}, totalSize: {totalSize}"); @@ -404,5 +411,4 @@ public class JustifierTests (ITestOutputHelper output) return output.ToString (); } - } From 13134df595d448095851c4152213271aed069fba Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 21 Apr 2024 00:02:26 -0600 Subject: [PATCH 06/11] Prototype Pos.Justify --- Terminal.Gui/Resources/config.json | 2 +- Terminal.Gui/View/Layout/PosDim.cs | 430 +++++++++++------ Terminal.Gui/View/Layout/ViewLayout.cs | 8 +- Terminal.Gui/Views/Dialog.cs | 14 +- Terminal.Gui/Views/Wizard/Wizard.cs | 2 +- UICatalog/Scenarios/ComputedLayout.cs | 8 +- UICatalog/Scenarios/Dialogs.cs | 4 +- UnitTests/Configuration/ThemeScopeTests.cs | 6 +- UnitTests/Configuration/ThemeTests.cs | 6 +- UnitTests/Dialogs/DialogTests.cs | 86 ++-- UnitTests/View/Layout/DimTests.cs | 533 ++++++++++++++++----- UnitTests/View/Layout/PosTests.cs | 230 +++++++-- 12 files changed, 953 insertions(+), 376 deletions(-) diff --git a/Terminal.Gui/Resources/config.json b/Terminal.Gui/Resources/config.json index 368ccd8bf..4f82e43d6 100644 --- a/Terminal.Gui/Resources/config.json +++ b/Terminal.Gui/Resources/config.json @@ -24,7 +24,7 @@ "Themes": [ { "Default": { - "Dialog.DefaultButtonAlignment": "Center", + "Dialog.DefaultButtonAlignment": "Centered", "FrameView.DefaultBorderStyle": "Single", "Window.DefaultBorderStyle": "Single", "ColorSchemes": [ diff --git a/Terminal.Gui/View/Layout/PosDim.cs b/Terminal.Gui/View/Layout/PosDim.cs index 37f2a5bf0..1147e96dc 100644 --- a/Terminal.Gui/View/Layout/PosDim.cs +++ b/Terminal.Gui/View/Layout/PosDim.cs @@ -1,23 +1,10 @@ +using System.Diagnostics; +using static System.Net.Mime.MediaTypeNames; using static Terminal.Gui.Dialog; +using static Terminal.Gui.Dim; namespace Terminal.Gui; -/// Determines the horizontal alignment of Views. -public enum ViewAlignments -{ - /// Center-aligns the buttons (the default). - Center = 0, - - /// Justifies the buttons - Justify, - - /// Left-aligns the buttons - Left, - - /// Right-aligns the buttons - Right -} - /// /// Describes the position of a which can be an absolute value, a percentage, centered, or /// relative to the ending dimension. Integer values are implicitly convertible to an absolute . These @@ -203,12 +190,6 @@ public class Pos /// public static Pos Center () { return new PosCenter (); } - public static Pos Justify (View[] views, ViewAlignments alignment) - { - return new PosJustify (views, alignment); - } - - /// Determines whether the specified object is equal to the current object. /// The object to compare with the current object. /// @@ -225,6 +206,14 @@ public class Pos /// The returned from the function. public static Pos Function (Func function) { return new PosFunc (function); } + /// + /// Creates a object that justifies a set of views according to the specified justification. + /// + /// + /// + /// + public static Pos Justify ( Justification justification) { return new PosJustify (justification); } + /// Serves as the default hash function. /// A hash code for the current object. public override int GetHashCode () { return Anchor (0).GetHashCode (); } @@ -361,14 +350,17 @@ public class Pos /// height for y-coordinate calculation. /// /// The dimension of the View. It could be the current width or height. - /// Obsolete; to be deprecated. - /// Obsolete; to be deprecated. + /// The View that holds this Pos object. + /// Width or Height /// /// The calculated position of the View. The way this position is calculated depends on the specific subclass of Pos /// that /// is used. /// - internal virtual int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize) { return Anchor (superviewDimension); } + internal virtual int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension) + { + return Anchor (superviewDimension); + } internal class PosAbsolute (int n) : Pos { @@ -404,7 +396,7 @@ public class Pos return width - _offset; } - internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize) + internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension) { int newLocation = Anchor (superviewDimension); @@ -422,9 +414,9 @@ public class 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) + internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension) { - int newDimension = Math.Max (dim.Calculate (0, superviewDimension, autosize, autoSize), 0); + int newDimension = Math.Max (dim.Calculate (0, superviewDimension, us, dimension), 0); return Anchor (superviewDimension - newDimension); } @@ -450,11 +442,11 @@ public class Pos return la - ra; } - internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize) + internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension) { - int newDimension = dim.Calculate (0, superviewDimension, autosize, autoSize); - int left = _left.Calculate (superviewDimension, dim, autosize, autoSize); - int right = _right.Calculate (superviewDimension, dim, autosize, autoSize); + int newDimension = dim.Calculate (0, superviewDimension, us, dimension); + int left = _left.Calculate (superviewDimension, dim, us, dimension); + int right = _right.Calculate (superviewDimension, dim, us, dimension); if (_add) { @@ -474,6 +466,79 @@ public class Pos internal override int Anchor (int width) { return (int)(width * _factor); } } + + /// + /// Enables justification of a set of views. + /// + public class PosJustify : Pos + { + private readonly Justification _justification; + + /// + /// Enables justification of a set of views. + /// + /// The set of views to justify according to . + /// + public PosJustify (Justification justification) + { + _justification = justification; + } + + public override bool Equals (object other) + { + return other is PosJustify justify && justify._justification == _justification; + } + + public override int GetHashCode () { return _justification.GetHashCode (); } + + + public override string ToString () + { + return $"Justify(alignment={_justification})"; + } + + internal override int Anchor (int width) + { + return width; + } + + internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension) + { + // Find all the views that are being justified - they have the same justification and opposite position as us + // Use linq to filter us.Superview.Subviews that match `dimension` and are at our same location in the opposite dimension (e.g. if dimension is Width, filter by Y) + // Then, pass the array of views to the Justify method + int [] dimensions; + int [] positions; + + int ourIndex = 0; + if (dimension == Dimension.Width) + { + List dimensionsList = new List (); + for (int i = 0; i < us.SuperView.Subviews.Count; i++) + { + if (us.SuperView.Subviews [i].Frame.Y == us.Frame.Y) + { + dimensionsList.Add (us.SuperView.Subviews [i].Frame.Width); + + if (us.SuperView.Subviews [i] == us) + { + ourIndex = dimensionsList.Count - 1; + } + } + } + dimensions = dimensionsList.ToArray (); + positions = new Justifier ().Justify (dimensions, _justification, superviewDimension); + } + else + { + dimensions = us.SuperView.Subviews.Where (v => v.Frame.X == us.Frame.X).Select(v => v.Frame.Height ).ToArray (); + positions = new Justifier ().Justify (dimensions, _justification, superviewDimension); + } + + return positions [ourIndex]; + } + + } // Helper class to provide dynamic value by the execution of a function that returns an integer. internal class PosFunc (Func n) : Pos { @@ -484,11 +549,29 @@ public class Pos internal override int Anchor (int width) { return _function (); } } + /// + /// Describes which side of the view to use for the position. + /// public enum Side { + /// + /// The left (X) side of the view. + /// Left = 0, + + /// + /// The top (Y) side of the view. + /// Top = 1, + + /// + /// The right (X + Width) side of the view. + /// Right = 2, + + /// + /// The bottom (Y + Height) side of the view. + /// Bottom = 3 } @@ -503,8 +586,8 @@ public class Pos { string sideString = side switch { - Side.Left => "x", - Side.Top => "y", + Side.Left => "left", + Side.Top => "top", Side.Right => "right", Side.Bottom => "bottom", _ => "unknown" @@ -530,92 +613,6 @@ public class Pos }; } } - - - /// - /// Enables justification of a set of views. - /// - public class PosJustify : Pos - { - private readonly View [] _views; - private readonly ViewAlignments _alignment; - - /// - /// Enables justification of a set of views. - /// - /// The set of views to justify according to . - /// - public PosJustify (View [] views, ViewAlignments alignment) - { - _alignment = alignment; - _views = views; - } - - public override bool Equals (object other) - { - return other is PosJustify justify && justify._views == _views && justify._alignment == _alignment; - } - - public override int GetHashCode () { return _views.GetHashCode (); } - - - public override string ToString () - { - return $"Justify(views={_views},alignment={_alignment})"; - } - - internal override int Anchor (int width) - { - if (_views.Length == 0 || !_views [0].IsInitialized) - { - return 0; - } - int spacing = 0; - switch (_alignment) - { - case ViewAlignments.Center: - // Center spacing is sum of the widths of the views - width / number of views - spacing = (width - _views.Select (v => v.Frame.Width).Sum ()) / _views.Length; - - // How do I know which view we are? - View us = _views.Where (v => v.X.Equals (this)).First(); - - if (_views [0] == us) - { - return spacing; - } - // Calculate the position of the previous (left or above us) view - int previous = _views.Where (v => v.X.Equals (us)).First().Frame.Left; - - return previous + spacing; - //case ViewAlignments.Left: - // return Left (width); - //case ViewAlignments.Right: - // return Right (width); - //case ViewAlignments.Justify: - // return Justify (width); - default: - return 0; - - } - } - - //internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize) - //{ - // // Assuming autosize is the size that the View would have if it were to automatically adjust its size based on its content - // // and autoSize is a boolean value that indicates whether the View should automatically adjust its size based on its content - // if (autoSize) - // { - // return autosize; - // } - // else - // { - // // Assuming dim.Calculate returns the calculated size of the View - // return dim.Calculate (_views.Frame.Left, _views.Frame.Width, autosize, autoSize); - // } - //} - - } } /// @@ -638,6 +635,15 @@ public class Pos /// /// /// +/// +/// +/// +/// Creates a object that automatically sizes the view to fit +/// the view's SubViews. +/// +/// +/// +/// /// /// /// @@ -687,6 +693,85 @@ public class Pos /// public class Dim { + /// + /// Specifies how will compute the dimension. + /// + public enum DimAutoStyle + { + /// + /// The dimension will be computed using both the view's and + /// (whichever is larger). + /// + Auto, + + /// + /// The Subview in with the largest corresponding position plus dimension + /// will determine the dimension. + /// The corresponding dimension of the view's will be ignored. + /// + Subviews, + + /// + /// The corresponding dimension of the view's , formatted using the + /// settings, + /// will be used to determine the dimension. + /// The corresponding dimensions of the will be ignored. + /// + Text + } + + + /// + /// + /// + public enum Dimension + { + /// + /// No dimension specified. + /// + None = 0, + + /// + /// The height dimension. + /// + Height = 1, + + /// + /// The width dimension. + /// + Width = 2 + } + + + /// + /// Creates a object that automatically sizes the view to fit all of the view's SubViews and/or Text. + /// + /// + /// This initializes a with two SubViews. The view will be automatically sized to fit the two + /// SubViews. + /// + /// var button = new Button () { Text = "Click Me!", X = 1, Y = 1, Width = 10, Height = 1 }; + /// var textField = new TextField { Text = "Type here", X = 1, Y = 2, Width = 20, Height = 1 }; + /// var view = new Window () { Title = "MyWindow", X = 0, Y = 0, Width = Dim.Auto (), Height = Dim.Auto () }; + /// view.Add (button, textField); + /// + /// + /// The object. + /// + /// Specifies how will compute the dimension. The default is . + /// + /// Specifies the minimum dimension that view will be automatically sized to. + /// Specifies the maximum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED. + public static Dim Auto (DimAutoStyle style = DimAutoStyle.Auto, Dim min = null, Dim max = null) + { + if (max != null) + { + throw new NotImplementedException (@"max is not implemented"); + } + + return new DimAuto (style, min, max); + } + /// Determines whether the specified object is equal to the current object. /// The object to compare with the current object. /// @@ -817,24 +902,22 @@ public class Dim /// /// Calculates and returns the dimension of a object. It takes into account the location of the - /// , its current size, and whether it should automatically adjust its size based on its content. + /// , it's SuperView's ContentSize, and whether it should automatically adjust its size based on its content. /// /// /// 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. /// - /// The current size of the View. It could be the current width or height. - /// Obsolete; To be deprecated. - /// Obsolete; To be deprecated. + /// The size of the SuperView's content. It could be width or height. + /// The View that holds this Pos object. + /// Width or Height /// /// The calculated size of the View. The way this size is calculated depends on the specific subclass of Dim that /// is used. /// - internal virtual int Calculate (int location, int dimension, int autosize, bool autoSize) + internal virtual int Calculate (int location, int superviewContentSize, View us, Dimension dimension) { - int newDimension = Math.Max (Anchor (dimension - location), 0); - - return autoSize && autosize > newDimension ? autosize : newDimension; + return Math.Max (Anchor (superviewContentSize - location), 0); } internal class DimAbsolute (int n) : Dim @@ -845,15 +928,75 @@ public class Dim 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) + internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension) { // DimAbsolute.Anchor (int width) ignores width and returns n - int newDimension = Math.Max (Anchor (0), 0); - - return autoSize && autosize > newDimension ? autosize : newDimension; + return Math.Max (Anchor (0), 0); } } + internal class DimAuto (DimAutoStyle style, Dim min, Dim max) : Dim + { + internal readonly Dim _max = max; + internal readonly Dim _min = min; + internal readonly DimAutoStyle _style = style; + internal int Size; + + public override bool Equals (object other) { return other is DimAuto auto && auto._min == _min && auto._max == _max && auto._style == _style; } + public override int GetHashCode () { return HashCode.Combine (base.GetHashCode (), _min, _max, _style); } + public override string ToString () { return $"Auto({_style},{_min},{_max})"; } + + internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension) + { + if (us == null) + { + return _max?.Anchor (0) ?? 0; + } + + var textSize = 0; + var subviewsSize = 0; + + int autoMin = _min?.Anchor (superviewContentSize) ?? 0; + + if (superviewContentSize < autoMin) + { + Debug.WriteLine ($"WARNING: DimAuto specifies a min size ({autoMin}), but the SuperView's bounds are smaller ({superviewContentSize})."); + + return superviewContentSize; + } + + if (_style is Dim.DimAutoStyle.Text or Dim.DimAutoStyle.Auto) + { + textSize = int.Max (autoMin, dimension == Dimension.Width ? us.TextFormatter.Size.Width : us.TextFormatter.Size.Height); + } + + if (_style is Dim.DimAutoStyle.Subviews or Dim.DimAutoStyle.Auto) + { + subviewsSize = us.Subviews.Count == 0 + ? 0 + : us.Subviews + .Where (v => dimension == Dimension.Width ? v.X is not Pos.PosAnchorEnd : v.Y is not Pos.PosAnchorEnd) + .Max (v => dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height); + } + + int max = int.Max (textSize, subviewsSize); + + Thickness thickness = us.GetAdornmentsThickness (); + + if (dimension == Dimension.Width) + { + max += thickness.Horizontal; + } + else + { + max += thickness.Vertical; + } + + max = int.Max (max, autoMin); + return int.Min (max, _max?.Anchor (superviewContentSize) ?? superviewContentSize); + } + + } internal class DimCombine (bool add, Dim left, Dim right) : Dim { internal bool _add = add; @@ -874,10 +1017,10 @@ public class Dim return la - ra; } - internal override int Calculate (int location, int dimension, int autosize, bool autoSize) + internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension) { - int leftNewDim = _left.Calculate (location, dimension, autosize, autoSize); - int rightNewDim = _right.Calculate (location, dimension, autosize, autoSize); + int leftNewDim = _left.Calculate (location, superviewContentSize, us, dimension); + int rightNewDim = _right.Calculate (location, superviewContentSize, us, dimension); int newDimension; @@ -890,8 +1033,9 @@ public class Dim newDimension = Math.Max (0, leftNewDim - rightNewDim); } - return autoSize && autosize > newDimension ? autosize : newDimension; + return newDimension; } + } internal class DimFactor (float factor, bool remaining = false) : Dim @@ -905,11 +1049,9 @@ public class Dim 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) + internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension) { - int newDimension = _remaining ? Math.Max (Anchor (dimension - location), 0) : Anchor (dimension); - - return autoSize && autosize > newDimension ? autosize : newDimension; + return _remaining ? Math.Max (Anchor (superviewContentSize - location), 0) : Anchor (superviewContentSize); } } @@ -932,12 +1074,6 @@ public class Dim internal override int Anchor (int width) { return _function (); } } - public enum Dimension - { - Height = 0, - Width = 1 - } - internal class DimView : Dim { private readonly Dimension _side; @@ -979,4 +1115,4 @@ public class Dim }; } } -} +} \ No newline at end of file diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs index 5c6f0cc64..c20e012dd 100644 --- a/Terminal.Gui/View/Layout/ViewLayout.cs +++ b/Terminal.Gui/View/Layout/ViewLayout.cs @@ -1025,10 +1025,10 @@ public partial class View autoSize = GetAutoSize (); } - 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); + int newX = _x.Calculate (superviewContentSize.Width, _width, this, Dim.Dimension.Width); + int newW = _width.Calculate (newX, superviewContentSize.Width, this, Dim.Dimension.Width); + int newY = _y.Calculate (superviewContentSize.Height, _height, this, Dim.Dimension.Height); + int newH = _height.Calculate (newY, superviewContentSize.Height, this, Dim.Dimension.Height); Rectangle newFrame = new (newX, newY, newW, newH); diff --git a/Terminal.Gui/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs index cd859ac0b..564af7245 100644 --- a/Terminal.Gui/Views/Dialog.cs +++ b/Terminal.Gui/Views/Dialog.cs @@ -94,7 +94,7 @@ public class Dialog : Window } /// Determines how the s are aligned along the bottom of the dialog. - public ViewAlignments ButtonAlignment { get; set; } + public Justification ButtonAlignment { get; set; } /// Optional buttons to lay out at the bottom of the dialog. public Button [] Buttons @@ -114,11 +114,11 @@ public class Dialog : Window } } - /// The default for . + /// The default for . /// This property can be set in a Theme. [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] [JsonConverter (typeof (JsonStringEnumConverter))] - public static ViewAlignments DefaultButtonAlignment { get; set; } = ViewAlignments.Center; + public static Justification DefaultButtonAlignment { get; set; } = Justification.Centered; /// /// Adds a to the , its layout will be controlled by the @@ -185,7 +185,7 @@ public class Dialog : Window switch (ButtonAlignment) { - case ViewAlignments.Center: + case Justification.Centered: // Center Buttons shiftLeft = (Viewport.Width - buttonsWidth - _buttons.Count - 1) / 2 + 1; @@ -208,7 +208,7 @@ public class Dialog : Window break; - case ViewAlignments.Justify: + case Justification.Justified: // Justify Buttons // leftmost and rightmost buttons are hard against edges. The rest are evenly spaced. @@ -243,7 +243,7 @@ public class Dialog : Window break; - case ViewAlignments.Left: + case Justification.Left: // Left Align Buttons Button prevButton = _buttons [0]; prevButton.X = 0; @@ -259,7 +259,7 @@ public class Dialog : Window break; - case ViewAlignments.Right: + case Justification.Right: // Right align buttons shiftLeft = _buttons [_buttons.Count - 1].Frame.Width; _buttons [_buttons.Count - 1].X = Pos.AnchorEnd (shiftLeft); diff --git a/Terminal.Gui/Views/Wizard/Wizard.cs b/Terminal.Gui/Views/Wizard/Wizard.cs index eddef7f4c..e77d4cfe5 100644 --- a/Terminal.Gui/Views/Wizard/Wizard.cs +++ b/Terminal.Gui/Views/Wizard/Wizard.cs @@ -85,7 +85,7 @@ public class Wizard : Dialog { // Using Justify causes the Back and Next buttons to be hard justified against // the left and right edge - ButtonAlignment = ViewAlignments.Justify; + ButtonAlignment = Justification.Justified; BorderStyle = LineStyle.Double; //// Add a horiz separator diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs index fea26eab9..cd8f36ee4 100644 --- a/UICatalog/Scenarios/ComputedLayout.cs +++ b/UICatalog/Scenarios/ComputedLayout.cs @@ -408,10 +408,10 @@ public class ComputedLayout : Scenario app.Add (rightButton); - // Center three buttons with 5 spaces between them - leftButton.X = Pos.Justify (buttons, ViewAlignments.Center); - centerButton.X = Pos.Justify (buttons, ViewAlignments.Center); - rightButton.X = Pos.Justify (buttons, ViewAlignments.Center); + // Center three buttons with + leftButton.X = Pos.Justify (Justification.Centered); + centerButton.X = Pos.Justify (Justification.Centered); + rightButton.X = Pos.Justify (Justification.Centered); Application.Run (app); app.Dispose (); diff --git a/UICatalog/Scenarios/Dialogs.cs b/UICatalog/Scenarios/Dialogs.cs index cc2399317..802591aad 100644 --- a/UICatalog/Scenarios/Dialogs.cs +++ b/UICatalog/Scenarios/Dialogs.cs @@ -137,7 +137,7 @@ public class Dialogs : Scenario { X = Pos.Right (label) + 1, Y = Pos.Top (label), - RadioLabels = new [] { "_Center", "_Justify", "_Left", "_Right" } + RadioLabels = new [] { "_Centered", "_Justified", "_Left", "_Right" } }; frame.Add (styleRadioGroup); @@ -265,7 +265,7 @@ public class Dialogs : Scenario dialog = new Dialog { Title = titleEdit.Text, - ButtonAlignment = (ViewAlignments)styleRadioGroup.SelectedItem, + ButtonAlignment = (Justification)styleRadioGroup.SelectedItem, Buttons = buttons.ToArray () }; diff --git a/UnitTests/Configuration/ThemeScopeTests.cs b/UnitTests/Configuration/ThemeScopeTests.cs index b22ae60ee..0eecd05b7 100644 --- a/UnitTests/Configuration/ThemeScopeTests.cs +++ b/UnitTests/Configuration/ThemeScopeTests.cs @@ -29,12 +29,12 @@ public class ThemeScopeTests { Reset (); Assert.NotEmpty (Themes); - Assert.Equal (ViewAlignments.Center, Dialog.DefaultButtonAlignment); + Assert.Equal (Justification.Centered, Dialog.DefaultButtonAlignment); - Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = ViewAlignments.Right; + Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = Justification.Right; ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply (); - Assert.Equal (ViewAlignments.Right, Dialog.DefaultButtonAlignment); + Assert.Equal (Justification.Right, Dialog.DefaultButtonAlignment); Reset (); } diff --git a/UnitTests/Configuration/ThemeTests.cs b/UnitTests/Configuration/ThemeTests.cs index c9c2ba2bb..5ecee6bbe 100644 --- a/UnitTests/Configuration/ThemeTests.cs +++ b/UnitTests/Configuration/ThemeTests.cs @@ -77,15 +77,15 @@ public class ThemeTests public void TestSerialize_RoundTrip () { var theme = new ThemeScope (); - theme ["Dialog.DefaultButtonAlignment"].PropertyValue = ViewAlignments.Right; + theme ["Dialog.DefaultButtonAlignment"].PropertyValue = Justification.Right; string json = JsonSerializer.Serialize (theme, _jsonOptions); var deserialized = JsonSerializer.Deserialize (json, _jsonOptions); Assert.Equal ( - ViewAlignments.Right, - (ViewAlignments)deserialized ["Dialog.DefaultButtonAlignment"].PropertyValue + Justification.Right, + (Justification)deserialized ["Dialog.DefaultButtonAlignment"].PropertyValue ); Reset (); } diff --git a/UnitTests/Dialogs/DialogTests.cs b/UnitTests/Dialogs/DialogTests.cs index f74ea5b12..04f859ed0 100644 --- a/UnitTests/Dialogs/DialogTests.cs +++ b/UnitTests/Dialogs/DialogTests.cs @@ -32,7 +32,7 @@ public class DialogTests Title = title, Width = width, Height = 1, - ButtonAlignment = ViewAlignments.Center, + ButtonAlignment = Justification.Centered, Buttons = [new Button { Text = btn1Text }] }; @@ -57,7 +57,7 @@ public class DialogTests Title = title, Width = width, Height = 1, - ButtonAlignment = ViewAlignments.Justify, + ButtonAlignment = Justification.Justified, Buttons = [new Button { Text = btn1Text }] }; @@ -82,7 +82,7 @@ public class DialogTests Title = title, Width = width, Height = 1, - ButtonAlignment = ViewAlignments.Right, + ButtonAlignment = Justification.Right, Buttons = [new Button { Text = btn1Text }] }; @@ -107,7 +107,7 @@ public class DialogTests Title = title, Width = width, Height = 1, - ButtonAlignment = ViewAlignments.Left, + ButtonAlignment = Justification.Left, Buttons = [new Button { Text = btn1Text }] }; @@ -155,7 +155,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Center, + Justification.Centered, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -172,7 +172,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Justify, + Justification.Justified, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -189,7 +189,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Right, + Justification.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -206,7 +206,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Left, + Justification.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -248,7 +248,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Center, + Justification.Centered, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -280,7 +280,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Justify, + Justification.Justified, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -296,7 +296,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Right, + Justification.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -312,7 +312,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Left, + Justification.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -353,7 +353,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Center, + Justification.Centered, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -370,7 +370,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Justify, + Justification.Justified, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -387,7 +387,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Right, + Justification.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -404,7 +404,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Left, + Justification.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -447,7 +447,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Center, + Justification.Centered, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -464,7 +464,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Justify, + Justification.Justified, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -481,7 +481,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Right, + Justification.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -498,7 +498,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Left, + Justification.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text }, @@ -530,7 +530,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Center, + Justification.Centered, new Button { Text = btnText } ); @@ -547,7 +547,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Justify, + Justification.Justified, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -562,7 +562,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Right, + Justification.Right, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -577,7 +577,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Left, + Justification.Left, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -594,7 +594,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Center, + Justification.Centered, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -609,7 +609,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Justify, + Justification.Justified, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -624,7 +624,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Right, + Justification.Right, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -639,7 +639,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Left, + Justification.Left, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -673,7 +673,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Center, + Justification.Centered, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text } @@ -689,7 +689,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Justify, + Justification.Justified, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text } @@ -705,7 +705,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Right, + Justification.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text } @@ -721,7 +721,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Left, + Justification.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text }, new Button { Text = btn3Text } @@ -755,7 +755,7 @@ public class DialogTests (runstate, Dialog dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Center, + Justification.Centered, new Button { Text = btn1Text }, new Button { Text = btn2Text } ); @@ -770,7 +770,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Justify, + Justification.Justified, new Button { Text = btn1Text }, new Button { Text = btn2Text } ); @@ -785,7 +785,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Right, + Justification.Right, new Button { Text = btn1Text }, new Button { Text = btn2Text } ); @@ -800,7 +800,7 @@ public class DialogTests (runstate, dlg) = RunButtonTestDialog ( title, width, - ViewAlignments.Left, + Justification.Left, new Button { Text = btn1Text }, new Button { Text = btn2Text } ); @@ -837,7 +837,7 @@ public class DialogTests // Default (Center) button1 = new Button { Text = btn1Text }; button2 = new Button { Text = btn2Text }; - (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Center, button1, button2); + (runstate, dlg) = RunButtonTestDialog (title, width, Justification.Centered, button1, button2); button1.Visible = false; RunIteration (ref runstate, ref firstIteration); buttonRow = $@"{CM.Glyphs.VLine} {btn2} {CM.Glyphs.VLine}"; @@ -849,7 +849,7 @@ public class DialogTests Assert.Equal (width, buttonRow.Length); button1 = new Button { Text = btn1Text }; button2 = new Button { Text = btn2Text }; - (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Justify, button1, button2); + (runstate, dlg) = RunButtonTestDialog (title, width, Justification.Justified, button1, button2); button1.Visible = false; RunIteration (ref runstate, ref firstIteration); buttonRow = $@"{CM.Glyphs.VLine} {btn2}{CM.Glyphs.VLine}"; @@ -861,7 +861,7 @@ public class DialogTests Assert.Equal (width, buttonRow.Length); button1 = new Button { Text = btn1Text }; button2 = new Button { Text = btn2Text }; - (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Right, button1, button2); + (runstate, dlg) = RunButtonTestDialog (title, width, Justification.Right, button1, button2); button1.Visible = false; RunIteration (ref runstate, ref firstIteration); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -872,7 +872,7 @@ public class DialogTests Assert.Equal (width, buttonRow.Length); button1 = new Button { Text = btn1Text }; button2 = new Button { Text = btn2Text }; - (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Left, button1, button2); + (runstate, dlg) = RunButtonTestDialog (title, width, Justification.Left, button1, button2); button1.Visible = false; RunIteration (ref runstate, ref firstIteration); buttonRow = $@"{CM.Glyphs.VLine} {btn2} {CM.Glyphs.VLine}"; @@ -1301,7 +1301,7 @@ public class DialogTests (runstate, Dialog _) = RunButtonTestDialog ( title, width, - ViewAlignments.Center, + Justification.Centered, new Button { Text = btnText } ); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); @@ -1347,7 +1347,7 @@ public class DialogTests int width = buttonRow.Length; d.SetBufferSize (buttonRow.Length, 3); - (runstate, Dialog _) = RunButtonTestDialog (title, width, ViewAlignments.Center, null); + (runstate, Dialog _) = RunButtonTestDialog (title, width, Justification.Centered, null); TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output); End (runstate); @@ -1356,7 +1356,7 @@ public class DialogTests private (RunState, Dialog) RunButtonTestDialog ( string title, int width, - ViewAlignments align, + Justification align, params Button [] btns ) { diff --git a/UnitTests/View/Layout/DimTests.cs b/UnitTests/View/Layout/DimTests.cs index c1a529a72..04f636c80 100644 --- a/UnitTests/View/Layout/DimTests.cs +++ b/UnitTests/View/Layout/DimTests.cs @@ -25,61 +25,62 @@ public class DimTests } [Fact] - public void DimAbsolute_GetDimension_ReturnsCorrectValue () + public void DimAbsolute_Calculate_ReturnsCorrectValue () { var dim = new DimAbsolute (10); - var result = dim.Calculate (0, 100, 50, false); + var result = dim.Calculate (0, 100, null, Dim.Dimension.None); Assert.Equal (10, result); } [Fact] - public void DimCombine_GetDimension_ReturnsCorrectValue () + public void DimCombine_Calculate_ReturnsCorrectValue () { var dim1 = new DimAbsolute (10); var dim2 = new DimAbsolute (20); var dim = dim1 + dim2; - var result = dim.Calculate (0, 100, 50, false); + var result = dim.Calculate (0, 100, null, Dim.Dimension.None); Assert.Equal (30, result); } [Fact] - public void DimFactor_GetDimension_ReturnsCorrectValue () + public void DimFactor_Calculate_ReturnsCorrectValue () { var dim = new DimFactor (0.5f); - var result = dim.Calculate (0, 100, 50, false); + var result = dim.Calculate (0, 100, null, Dim.Dimension.None); Assert.Equal (50, result); } [Fact] - public void DimFill_GetDimension_ReturnsCorrectValue () + public void DimFill_Calculate_ReturnsCorrectValue () { var dim = Dim.Fill (); - var result = dim.Calculate (0, 100, 50, false); + var result = dim.Calculate (0, 100, null, Dim.Dimension.None); Assert.Equal (100, result); } [Fact] - public void DimFunc_GetDimension_ReturnsCorrectValue () + public void DimFunc_Calculate_ReturnsCorrectValue () { var dim = new DimFunc (() => 10); - var result = dim.Calculate (0, 100, 50, false); + var result = dim.Calculate (0, 100, null, Dim.Dimension.None); Assert.Equal (10, result); } [Fact] - public void DimView_GetDimension_ReturnsCorrectValue () + public void DimView_Calculate_ReturnsCorrectValue () { var view = new View { Width = 10 }; var dim = new DimView (view, Dimension.Width); - var result = dim.Calculate (0, 100, 50, false); + var result = dim.Calculate (0, 100, null, Dim.Dimension.None); 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] [AutoInitShutdown] - public void Dim_Add_Operator () + public void Add_Operator () { Toplevel top = new (); @@ -372,7 +373,7 @@ public class DimTests /// This is an intentionally obtuse test. See https://github.com/gui-cs/Terminal.Gui/issues/2461 [Fact] [TestRespondersDisposed] - public void DimCombine_ObtuseScenario_Throw_If_SuperView_Refs_SubView () + public void Combine_ObtuseScenario_Throw_If_SuperView_Refs_SubView () { var t = new View { Width = 80, Height = 25 }; @@ -420,45 +421,65 @@ public class DimTests // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved // TODO: A new test that calls SetRelativeLayout directly is needed. - [Theory] - [AutoInitShutdown] - [InlineData (0, true)] - [InlineData (0, false)] - [InlineData (50, true)] - [InlineData (50, false)] - public void DimPercentPlusOne (int startingDistance, bool testHorizontal) + [Fact] + [TestRespondersDisposed] + public void Combine_View_Not_Added_Throws () { - var container = new View { Width = 100, Height = 100 }; + var t = new View { Width = 80, Height = 50 }; - var label = new Label - { - AutoSize = false, - X = testHorizontal ? startingDistance : 0, - Y = testHorizontal ? 0 : startingDistance, - Width = testHorizontal ? Dim.Percent (50) + 1 : 1, - Height = testHorizontal ? 1 : Dim.Percent (50) + 1 - }; + var super = new View { Width = Dim.Width (t) - 2, Height = Dim.Height (t) - 2 }; + t.Add (super); - container.Add (label); - var top = new Toplevel (); - top.Add (container); - top.BeginInit (); - top.EndInit (); - top.LayoutSubviews (); + var sub = new View (); + super.Add (sub); - Assert.Equal (100, container.Frame.Width); - Assert.Equal (100, container.Frame.Height); + var v1 = new View { Width = Dim.Width (super) - 2, Height = Dim.Height (super) - 2 }; + var v2 = new View { Width = Dim.Width (v1) - 2, Height = Dim.Height (v1) - 2 }; + sub.Add (v1); - if (testHorizontal) - { - Assert.Equal (51, label.Frame.Width); - Assert.Equal (1, label.Frame.Height); - } - else - { - Assert.Equal (1, label.Frame.Width); - Assert.Equal (51, label.Frame.Height); - } + // v2 not added to sub; should cause exception on Layout since it's referenced by sub. + sub.Width = Dim.Fill () - Dim.Width (v2); + sub.Height = Dim.Fill () - Dim.Height (v2); + + t.BeginInit (); + t.EndInit (); + + Assert.Throws (() => t.LayoutSubviews ()); + t.Dispose (); + v2.Dispose (); + } + + // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved + // TODO: A new test that calls SetRelativeLayout directly is needed. + [Fact] + [TestRespondersDisposed] + public void + Dim_Validation_Does_Not_Throw_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute () + { + var t = new View { Width = 80, Height = 25, Text = "top" }; + + var w = new Window { Width = Dim.Fill (), Height = Dim.Sized (10) }; + var v = new View { Width = Dim.Width (w) - 2, Height = Dim.Percent (10), Text = "v" }; + + w.Add (v); + t.Add (w); + + Assert.Equal (LayoutStyle.Absolute, t.LayoutStyle); + Assert.Equal (LayoutStyle.Computed, w.LayoutStyle); + Assert.Equal (LayoutStyle.Computed, v.LayoutStyle); + + t.LayoutSubviews (); + Assert.Equal (2, v.Width = 2); + Assert.Equal (2, v.Height = 2); + + // Force v to be LayoutStyle.Absolute; + v.Frame = new Rectangle (0, 1, 3, 4); + Assert.Equal (LayoutStyle.Absolute, v.LayoutStyle); + t.LayoutSubviews (); + + Assert.Equal (2, v.Width = 2); + Assert.Equal (2, v.Height = 2); + t.Dispose (); } [Fact] @@ -471,6 +492,90 @@ public class DimTests Assert.Equal (dim1, dim2); } + // Tests that Dim.Fill honors the margin parameter correctly + [Theory] + [InlineData (0, true, 25)] + [InlineData (0, false, 25)] + [InlineData (1, true, 24)] + [InlineData (1, false, 24)] + [InlineData (2, true, 23)] + [InlineData (2, false, 23)] + [InlineData (-2, true, 27)] + [InlineData (-2, false, 27)] + public void Fill_Margin (int margin, bool width, int expected) + { + var super = new View { Width = 25, Height = 25 }; + + var view = new View + { + X = 0, + Y = 0, + Width = width ? Dim.Fill (margin) : 1, + Height = width ? 1 : Dim.Fill (margin) + }; + + super.Add (view); + super.BeginInit (); + super.EndInit (); + super.LayoutSubviews (); + + Assert.Equal (25, super.Frame.Width); + Assert.Equal (25, super.Frame.Height); + + if (width) + { + Assert.Equal (expected, view.Frame.Width); + Assert.Equal (1, view.Frame.Height); + } + else + { + Assert.Equal (1, view.Frame.Width); + Assert.Equal (expected, view.Frame.Height); + } + } + + // Tests that Dim.Fill fills the dimension REMAINING from the View's X position to the end of the super view's width + [Theory] + [InlineData (0, true, 25)] + [InlineData (0, false, 25)] + [InlineData (1, true, 24)] + [InlineData (1, false, 24)] + [InlineData (2, true, 23)] + [InlineData (2, false, 23)] + [InlineData (-2, true, 27)] + [InlineData (-2, false, 27)] + public void Fill_Offset (int offset, bool width, int expected) + { + var super = new View { Width = 25, Height = 25 }; + + var view = new View + { + X = width ? offset : 0, + Y = width ? 0 : offset, + Width = width ? Dim.Fill () : 1, + Height = width ? 1 : Dim.Fill () + }; + + super.Add (view); + super.BeginInit (); + super.EndInit (); + super.LayoutSubviews (); + + Assert.Equal (25, super.Frame.Width); + Assert.Equal (25, super.Frame.Height); + + if (width) + { + Assert.Equal (expected, view.Frame.Width); + Assert.Equal (1, view.Frame.Height); + } + else + { + Assert.Equal (1, view.Frame.Width); + Assert.Equal (expected, view.Frame.Height); + } + } + // TODO: Other Dim.Height tests (e.g. Equal?) [Fact] @@ -670,45 +775,45 @@ public class DimTests t.Ready += (s, e) => { - Assert.Equal ("Absolute(100)", w.Width.ToString ()); - Assert.Equal ("Absolute(100)", w.Height.ToString ()); - Assert.Equal (100, w.Frame.Width); - Assert.Equal (100, w.Frame.Height); + Assert.Equal ("Absolute(100)", w.Width.ToString ()); + Assert.Equal ("Absolute(100)", w.Height.ToString ()); + Assert.Equal (100, w.Frame.Width); + Assert.Equal (100, w.Frame.Height); - Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ()); - Assert.Equal ("Absolute(5)", f1.Height.ToString ()); - Assert.Equal (49, f1.Frame.Width); // 50-1=49 - Assert.Equal (5, f1.Frame.Height); + Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ()); + Assert.Equal ("Absolute(5)", f1.Height.ToString ()); + Assert.Equal (49, f1.Frame.Width); // 50-1=49 + Assert.Equal (5, f1.Frame.Height); - Assert.Equal ("Fill(0)", f2.Width.ToString ()); - Assert.Equal ("Absolute(5)", f2.Height.ToString ()); - Assert.Equal (49, f2.Frame.Width); // 50-1=49 - Assert.Equal (5, f2.Frame.Height); + Assert.Equal ("Fill(0)", f2.Width.ToString ()); + Assert.Equal ("Absolute(5)", f2.Height.ToString ()); + Assert.Equal (49, f2.Frame.Width); // 50-1=49 + Assert.Equal (5, f2.Frame.Height); - #if DEBUG +#if DEBUG Assert.Equal ($"Combine(View(Width,FrameView(f1){f1.Border.Frame})-Absolute(2))", v1.Width.ToString ()); - #else +#else Assert.Equal ($"Combine(View(Width,FrameView(){f1.Border.Frame})-Absolute(2))", v1.Width.ToString ()); - #endif +#endif Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ()); Assert.Equal (47, v1.Frame.Width); // 49-2=47 Assert.Equal (89, v1.Frame.Height); // 98-5-2-2=89 - #if DEBUG +#if DEBUG Assert.Equal ( $"Combine(View(Width,FrameView(f2){f2.Frame})-Absolute(2))", v2.Width.ToString () - #else +#else Assert.Equal ( $"Combine(View(Width,FrameView(){f2.Frame})-Absolute(2))", v2.Width.ToString () - #endif +#endif ); - #if DEBUG +#if DEBUG Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ()); - #else +#else Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ()); - #endif +#endif Assert.Equal (47, v2.Frame.Width); // 49-2=47 Assert.Equal (89, v2.Frame.Height); // 98-5-2-2=89 @@ -757,22 +862,22 @@ public class DimTests Assert.Equal (5, f2.Frame.Height); v1.Text = "Button1"; - #if DEBUG +#if DEBUG Assert.Equal ($"Combine(View(Width,FrameView(f1){f1.Frame})-Absolute(2))", v1.Width.ToString ()); - #else +#else Assert.Equal ($"Combine(View(Width,FrameView(){f1.Frame})-Absolute(2))", v1.Width.ToString ()); - #endif +#endif Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ()); Assert.Equal (97, v1.Frame.Width); // 99-2=97 Assert.Equal (189, v1.Frame.Height); // 198-2-7=189 v2.Text = "Button2"; - #if DEBUG - Assert.Equal ( $"Combine(View(Width,FrameView(f2){f2.Frame})-Absolute(2))", v2.Width.ToString ()); - #else +#if DEBUG + Assert.Equal ($"Combine(View(Width,FrameView(f2){f2.Frame})-Absolute(2))", v2.Width.ToString ()); +#else Assert.Equal ($"Combine(View(Width,FrameView(){f2.Frame})-Absolute(2))", v2.Width.ToString ()); - #endif +#endif Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ()); Assert.Equal (97, v2.Frame.Width); // 99-2=97 Assert.Equal (189, v2.Frame.Height); // 198-2-7=189 @@ -782,7 +887,7 @@ public class DimTests Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ()); // 198*10%=19 * Percent is related to the super-view if it isn't null otherwise the view width - Assert.Equal (19, v3.Frame.Width ); + Assert.Equal (19, v3.Frame.Width); // 199*10%=19 Assert.Equal (19, v3.Frame.Height); @@ -793,20 +898,20 @@ public class DimTests Assert.Equal (50, v4.Frame.Width); Assert.Equal (50, v4.Frame.Height); v4.AutoSize = true; - Assert.Equal ("Absolute(11)", v4.Width.ToString ()); - Assert.Equal ("Absolute(1)", v4.Height.ToString ()); + Assert.Equal (Dim.Auto (DimAutoStyle.Text), v4.Width); + Assert.Equal (Dim.Auto (DimAutoStyle.Text), v4.Height); Assert.Equal (11, v4.Frame.Width); // 11 is the text length and because is Dim.DimAbsolute Assert.Equal (1, v4.Frame.Height); // 1 because is Dim.DimAbsolute v5.Text = "Button5"; - #if DEBUG +#if DEBUG Assert.Equal ($"Combine(View(Width,Button(v1){v1.Frame})-View(Width,Button(v3){v3.Frame}))", v5.Width.ToString ()); Assert.Equal ($"Combine(View(Height,Button(v1){v1.Frame})-View(Height,Button(v3){v3.Frame}))", v5.Height.ToString ()); - #else +#else Assert.Equal ($"Combine(View(Width,Button(){v1.Frame})-View(Width,Button(){v3.Frame}))", v5.Width.ToString ()); Assert.Equal ($"Combine(View(Height,Button(){v1.Frame})-View(Height,Button(){v3.Frame}))", v5.Height.ToString ()); - #endif +#endif Assert.Equal (78, v5.Frame.Width); // 97-9=78 Assert.Equal (170, v5.Frame.Height); // 189-19=170 @@ -880,6 +985,89 @@ public class DimTests Assert.Throws (() => dim = Dim.Percent (1000001)); } + [Theory] + [InlineData (0, false, true, 12)] + [InlineData (0, false, false, 12)] + [InlineData (1, false, true, 12)] + [InlineData (1, false, false, 12)] + [InlineData (2, false, true, 12)] + [InlineData (2, false, false, 12)] + + [InlineData (0, true, true, 12)] + [InlineData (0, true, false, 12)] + [InlineData (1, true, true, 12)] + [InlineData (1, true, false, 12)] + [InlineData (2, true, true, 11)] + [InlineData (2, true, false, 11)] + public void Percent_Position (int position, bool usePosition, bool width, int expected) + { + var super = new View { Width = 25, Height = 25 }; + + var view = new View + { + X = width ? position : 0, + Y = width ? 0 : position, + Width = width ? Dim.Percent (50, usePosition) : 1, + Height = width ? 1 : Dim.Percent (50, usePosition) + }; + + super.Add (view); + super.BeginInit (); + super.EndInit (); + super.LayoutSubviews (); + + Assert.Equal (25, super.Frame.Width); + Assert.Equal (25, super.Frame.Height); + + if (width) + { + Assert.Equal (expected, view.Frame.Width); + Assert.Equal (1, view.Frame.Height); + } + else + { + Assert.Equal (1, view.Frame.Width); + Assert.Equal (expected, view.Frame.Height); + } + } + + [Theory] + [InlineData (0, true)] + [InlineData (0, false)] + [InlineData (50, true)] + [InlineData (50, false)] + public void Percent_PlusOne (int startingDistance, bool testHorizontal) + { + var super = new View { Width = 100, Height = 100 }; + + var view = new View + { + X = testHorizontal ? startingDistance : 0, + Y = testHorizontal ? 0 : startingDistance, + Width = testHorizontal ? Dim.Percent (50) + 1 : 1, + Height = testHorizontal ? 1 : Dim.Percent (50) + 1 + }; + + super.Add (view); + super.BeginInit (); + super.EndInit (); + super.LayoutSubviews (); + + Assert.Equal (100, super.Frame.Width); + Assert.Equal (100, super.Frame.Height); + + if (testHorizontal) + { + Assert.Equal (51, view.Frame.Width); + Assert.Equal (1, view.Frame.Height); + } + else + { + Assert.Equal (1, view.Frame.Width); + Assert.Equal (51, view.Frame.Height); + } + } + [Fact] public void Percent_SetsValue () { @@ -898,47 +1086,24 @@ public class DimTests // TODO: A new test that calls SetRelativeLayout directly is needed. [Fact] [TestRespondersDisposed] - public void PosCombine_View_Not_Added_Throws () + public void Referencing_SuperView_Does_Not_Throw () { - var t = new View { Width = 80, Height = 50 }; + var super = new View { Width = 10, Height = 10, Text = "super" }; - var super = new View { Width = Dim.Width (t) - 2, Height = Dim.Height (t) - 2 }; - t.Add (super); + var view = new View + { + Width = Dim.Width (super), // this is allowed + Height = Dim.Height (super), // this is allowed + Text = "view" + }; - var sub = new View (); - super.Add (sub); + super.Add (view); + super.BeginInit (); + super.EndInit (); - var v1 = new View { Width = Dim.Width (super) - 2, Height = Dim.Height (super) - 2 }; - var v2 = new View { Width = Dim.Width (v1) - 2, Height = Dim.Height (v1) - 2 }; - sub.Add (v1); - - // v2 not added to sub; should cause exception on Layout since it's referenced by sub. - sub.Width = Dim.Fill () - Dim.Width (v2); - sub.Height = Dim.Fill () - Dim.Height (v2); - - t.BeginInit (); - t.EndInit (); - - Assert.Throws (() => t.LayoutSubviews ()); - t.Dispose (); - v2.Dispose (); - } - - [Fact] - [TestRespondersDisposed] - public void SetsValue () - { - var testVal = Rectangle.Empty; - var testValView = new View { Frame = testVal }; - Dim dim = Dim.Width (testValView); - Assert.Equal ($"View(Width,View(){testVal})", dim.ToString ()); - testValView.Dispose (); - - testVal = new Rectangle (1, 2, 3, 4); - testValView = new View { Frame = testVal }; - dim = Dim.Width (testValView); - Assert.Equal ($"View(Width,View(){testVal})", dim.ToString ()); - testValView.Dispose (); + Exception exception = Record.Exception (super.LayoutSubviews); + Assert.Null (exception); + super.Dispose (); } [Fact] @@ -982,6 +1147,119 @@ public class DimTests Assert.Equal ($"Absolute({testVal})", dim.ToString ()); } + // 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 Subtract_Operator () + { + Toplevel top = new Toplevel (); + + var view = new View { X = 0, Y = 0, Width = 20, Height = 0 }; + var field = new TextField { X = 0, Y = Pos.Bottom (view), Width = 20 }; + var count = 20; + List