From 84bbd5f25d721f8793ce5324aa8c12d9d8843c11 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 18 Dec 2023 13:39:21 -0700 Subject: [PATCH] Started on DimAuto unit tests --- Terminal.Gui/View/Layout/PosDim.cs | 18 +- Terminal.Gui/View/ViewLayout.cs | 27 +- UICatalog/Scenarios/DimAutoSize.cs | 2 +- UnitTests/View/Layout/DimAutoTests.cs | 361 +++++ UnitTests/View/Layout/DimTests.cs | 2023 +++++++++++++------------ UnitTests/View/Layout/PosTests.cs | 1741 +++++++++++---------- 6 files changed, 2272 insertions(+), 1900 deletions(-) create mode 100644 UnitTests/View/Layout/DimAutoTests.cs diff --git a/Terminal.Gui/View/Layout/PosDim.cs b/Terminal.Gui/View/Layout/PosDim.cs index e0998b624..cfbec1a17 100644 --- a/Terminal.Gui/View/Layout/PosDim.cs +++ b/Terminal.Gui/View/Layout/PosDim.cs @@ -255,28 +255,28 @@ public class Pos { public static Pos At (int n) => new PosAbsolute (n); internal class PosCombine : Pos { - internal Pos left, right; - internal bool add; + internal Pos _left, _right; + internal bool _add; public PosCombine (bool add, Pos left, Pos right) { - this.left = left; - this.right = right; - this.add = add; + _left = left; + _right = right; + _add = add; } internal override int Anchor (int width) { - int la = left.Anchor (width); - int ra = right.Anchor (width); - if (add) { + int la = _left.Anchor (width); + int ra = _right.Anchor (width); + if (_add) { return la + ra; } else { return la - ra; } } - public override string ToString () => $"Combine({left}{(add ? '+' : '-')}{right})"; + public override string ToString () => $"Combine({_left}{(_add ? '+' : '-')}{_right})"; } /// diff --git a/Terminal.Gui/View/ViewLayout.cs b/Terminal.Gui/View/ViewLayout.cs index d328df83a..e3d7becdb 100644 --- a/Terminal.Gui/View/ViewLayout.cs +++ b/Terminal.Gui/View/ViewLayout.cs @@ -404,7 +404,6 @@ namespace Terminal.Gui { return dim; } - /// /// Gets or sets whether validation of and occurs. /// @@ -427,14 +426,26 @@ namespace Terminal.Gui { void ThrowInvalid (View view, object checkPosDim, string name) { + // TODO: Figure out how to make CheckDimAuto deal with PosCombine object bad = null; switch (checkPosDim) { - case Pos pos and not Pos.PosAbsolute and not Pos.PosView: + case Pos pos and not Pos.PosAbsolute and not Pos.PosView and not Pos.PosCombine: bad = pos; break; - case Dim dim and not Dim.DimAbsolute and not Dim.DimView: + case Pos pos and Pos.PosCombine: + // Recursively check for not Absolute or not View + ThrowInvalid (view, (pos as Pos.PosCombine)._left, name); + ThrowInvalid (view, (pos as Pos.PosCombine)._right, name); + break; + + case Dim dim and not Dim.DimAbsolute and not Dim.DimView and not Dim.DimCombine: bad = dim; break; + case Dim dim and Dim.DimCombine: + // Recursively check for not Absolute or not View + ThrowInvalid (view, (dim as Dim.DimCombine)._left, name); + ThrowInvalid (view, (dim as Dim.DimCombine)._right, name); + break; } if (bad != null) { @@ -723,9 +734,9 @@ namespace Terminal.Gui { case Pos.PosCombine combine: int left, right; - (left, newDimension) = GetNewLocationAndDimension (width, superviewLocation, superviewDimension, combine.left, dim, autosizeDimension); - (right, newDimension) = GetNewLocationAndDimension (width, superviewLocation, superviewDimension, combine.right, dim, autosizeDimension); - if (combine.add) { + (left, newDimension) = GetNewLocationAndDimension (width, superviewLocation, superviewDimension, combine._left, dim, autosizeDimension); + (right, newDimension) = GetNewLocationAndDimension (width, superviewLocation, superviewDimension, combine._right, dim, autosizeDimension); + if (combine._add) { newLocation = left + right; } else { newLocation = left - right; @@ -861,8 +872,8 @@ namespace Terminal.Gui { } return; case Pos.PosCombine pc: - CollectPos (pc.left, from, ref nNodes, ref nEdges); - CollectPos (pc.right, from, ref nNodes, ref nEdges); + CollectPos (pc._left, from, ref nNodes, ref nEdges); + CollectPos (pc._right, from, ref nNodes, ref nEdges); break; } } diff --git a/UICatalog/Scenarios/DimAutoSize.cs b/UICatalog/Scenarios/DimAutoSize.cs index 7344c2881..27c1b1011 100644 --- a/UICatalog/Scenarios/DimAutoSize.cs +++ b/UICatalog/Scenarios/DimAutoSize.cs @@ -47,7 +47,7 @@ public class DimAutoSize : Scenario { var button = new Button () { Text = "Press to make button move down.", X = 0, Y = Pos.Bottom (label), - Width = Dim.Fill() + Width = 10 }; button.Clicked += (s, e) => { button.Y = button.Frame.Y + 1; diff --git a/UnitTests/View/Layout/DimAutoTests.cs b/UnitTests/View/Layout/DimAutoTests.cs new file mode 100644 index 000000000..d95db009c --- /dev/null +++ b/UnitTests/View/Layout/DimAutoTests.cs @@ -0,0 +1,361 @@ +using System; +using System.Globalization; +using System.Threading; +using Xunit; +using Xunit.Abstractions; + +// Alias Console to MockConsole so we don't accidentally use Console +using Console = Terminal.Gui.FakeConsole; + +namespace Terminal.Gui.ViewTests; + +public class DimAutoTests { + readonly ITestOutputHelper _output; + + public DimAutoTests (ITestOutputHelper output) + { + _output = output; + Console.OutputEncoding = System.Text.Encoding.Default; + // Change current culture + var culture = CultureInfo.CreateSpecificCulture ("en-US"); + Thread.CurrentThread.CurrentCulture = culture; + Thread.CurrentThread.CurrentUICulture = culture; + } + + [Fact] + public void NoSubViews_Does_Nothing () + { + var superView = new View () { + X = 0, + Y = 0, + Width = Dim.Auto (), + Height = Dim.Auto (), + ValidatePosDim = true, + }; + + superView.BeginInit (); + superView.EndInit (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Equal (new Rect (0, 0, 0, 0), superView.Frame); + + superView.SetRelativeLayout (new Rect (0, 0, 10, 10)); + Assert.Equal (new Rect (0, 0, 0, 0), superView.Frame); + + superView.SetRelativeLayout (new Rect (10, 10, 10, 10)); + Assert.Equal (new Rect (0, 0, 0, 0), superView.Frame); + } + + [Theory] + [InlineData (0, 0, 0, 0, 0, 0)] + [InlineData (0, 0, 5, 0, 5, 0)] + [InlineData (0, 0, 0, 5, 0, 5)] + [InlineData (0, 0, 5, 5, 5, 5)] + + [InlineData (1, 0, 5, 0, 6, 0)] + [InlineData (1, 0, 0, 5, 1, 5)] + [InlineData (1, 0, 5, 5, 6, 5)] + [InlineData (1, 1, 5, 5, 6, 6)] + + [InlineData (-1, 0, 5, 0, 4, 0)] + [InlineData (-1, 0, 0, 5, 0, 5)] + [InlineData (-1, 0, 5, 5, 4, 5)] + [InlineData (-1, -1, 5, 5, 4, 4)] + public void SubView_ChangesSuperViewSize (int subX, int subY, int subWidth, int subHeight, int expectedWidth, int expectedHeight) + { + var superView = new View () { + X = 0, + Y = 0, + Width = Dim.Auto (), + Height = Dim.Auto (), + ValidatePosDim = true, + }; + + var subView = new View () { + X = subX, + Y = subY, + Width = subWidth, + Height = subHeight, + ValidatePosDim = true, + }; + + superView.Add (subView); + + superView.BeginInit (); + superView.EndInit (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Equal (new Rect (0, 0, expectedWidth, expectedHeight), superView.Frame); + } + + [Theory] + [InlineData (0, 0, 0, 0, 0)] + [InlineData (0, 0, 5, 0, 5)] + [InlineData (0, 0, 0, 5, 0)] + [InlineData (0, 0, 5, 5, 5)] + + [InlineData (1, 0, 5, 0, 6)] + [InlineData (1, 0, 0, 5, 1)] + [InlineData (1, 0, 5, 5, 6)] + [InlineData (1, 1, 5, 5, 6)] + + [InlineData (-1, 0, 5, 0, 4)] + [InlineData (-1, 0, 0, 5, 0)] + [InlineData (-1, 0, 5, 5, 4)] + [InlineData (-1, -1, 5, 5, 4)] + public void Width_Auto_Height_NotChanged (int subX, int subY, int subWidth, int subHeight, int expectedWidth) + { + var superView = new View () { + X = 0, + Y = 0, + Width = Dim.Auto (), + Height = 10, + ValidatePosDim = true, + }; + + var subView = new View () { + X = subX, + Y = subY, + Width = subWidth, + Height = subHeight, + ValidatePosDim = true, + }; + + superView.Add (subView); + + superView.BeginInit (); + superView.EndInit (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Equal (new Rect (0, 0, expectedWidth, 10), superView.Frame); + } + + [Theory] + [InlineData (0, 0, 0, 0, 0)] + [InlineData (0, 0, 5, 0, 0)] + [InlineData (0, 0, 0, 5, 5)] + [InlineData (0, 0, 5, 5, 5)] + + [InlineData (1, 0, 5, 0, 0)] + [InlineData (1, 0, 0, 5, 5)] + [InlineData (1, 0, 5, 5, 5)] + [InlineData (1, 1, 5, 5, 6)] + + [InlineData (-1, 0, 5, 0, 0)] + [InlineData (-1, 0, 0, 5, 5)] + [InlineData (-1, 0, 5, 5, 5)] + [InlineData (-1, -1, 5, 5, 4)] + public void Height_Auto_Width_NotChanged (int subX, int subY, int subWidth, int subHeight, int expectedHeight) + { + var superView = new View () { + X = 0, + Y = 0, + Width = 10, + Height = Dim.Auto (), + ValidatePosDim = true, + }; + + var subView = new View () { + X = subX, + Y = subY, + Width = subWidth, + Height = subHeight, + ValidatePosDim = true, + }; + + superView.Add (subView); + + superView.BeginInit (); + superView.EndInit (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Equal (new Rect (0, 0, 10, expectedHeight), superView.Frame); + } + + // Test validation + [Fact] + public void ValidatePosDim_True_Throws_When_SubView_Uses_SuperView_Dims () + { + var superView = new View () { + X = 0, + Y = 0, + Width = Dim.Auto (), + Height = Dim.Auto (), + ValidatePosDim = true, + }; + + var subView = new View () { + X = 0, + Y = 0, + Width = Dim.Fill (), + Height = 10 + }; + + superView.Add (subView); + + superView.BeginInit (); + superView.EndInit (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.Width = 10; + subView.Height = Dim.Fill (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.Width = 10; + subView.Height = Dim.Percent(50); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.Width = 10; + subView.Height = 10; + subView.X = Pos.Center(); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.Width = 10; + subView.Height = 10; + subView.X = 0; + subView.Y = Pos.Center (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.Width = 10; + subView.Height = 10; + subView.X = 0; + subView.Y = 0; + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + superView.LayoutSubviews (); + } + + // Test validation + [Fact] + public void ValidatePosDim_True_Throws_When_SubView_Uses_SuperView_Dims_Combine () + { + var superView = new View () { + X = 0, + Y = 0, + Width = Dim.Auto (), + Height = Dim.Auto (), + ValidatePosDim = true, + }; + + var subView = new View () { + X = 0, + Y = 0, + Width = 10, + Height = 10 + }; + + + var subView2 = new View () { + X = 0, + Y = 0, + Width = 10, + Height = 10 + }; + + superView.Add (subView, subView2); + superView.BeginInit (); + superView.EndInit (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + superView.LayoutSubviews (); // no throw + + subView.Height = Dim.Fill () + 3; + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.Height = 3 + Dim.Fill (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.Height = 3 + 5 + Dim.Fill (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.Height = 3 + 5 + Dim.Percent (10); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + + // Tests nested Combine + subView.Height = 5 + new Dim.DimCombine (true, 3, new Dim.DimCombine (true, Dim.Percent(10), 9)); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + } + + [Fact] + public void ValidatePosDim_True_Throws_When_SubView_Uses_SuperView_Pos_Combine () + { + var superView = new View () { + X = 0, + Y = 0, + Width = Dim.Auto (), + Height = Dim.Auto (), + ValidatePosDim = true, + }; + + var subView = new View () { + X = 0, + Y = 0, + Width = 10, + Height = 10 + }; + + var subView2 = new View () { + X = 0, + Y = 0, + Width = 10, + Height = 10 + }; + + superView.Add (subView, subView2); + superView.BeginInit (); + superView.EndInit (); + + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + superView.LayoutSubviews (); // no throw + + subView.X = Pos.Right(subView2); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + superView.LayoutSubviews (); // no throw + + subView.X = 3 + Pos.Right (subView2); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); // no throw + superView.LayoutSubviews (); // no throw + + subView.X = Pos.Right (subView2) + 3; + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); // no throw + superView.LayoutSubviews (); // no throw + + subView.X = new Pos.PosCombine (true, Pos.Right (subView2), new Pos.PosCombine (true, 7, 9)); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); // no throw + + subView.X = Pos.Center () + 3; + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.X = 3 + Pos.Center (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.X = 3 + 5 + Pos.Center (); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.X = 3 + 5 + Pos.Percent (10); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + subView.X = Pos.Percent (10) + Pos.Center(); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + // Tests nested Combine + subView.X = 5 + new Pos.PosCombine (true, Pos.Right (subView2), new Pos.PosCombine (true, Pos.Center (), 9)); + superView.SetRelativeLayout (new Rect (0, 0, 0, 0)); + Assert.Throws (() => superView.LayoutSubviews ()); + + } + + // Test variations of Frame + + // test PosCombine (can DimAuto be combined??!) +} \ No newline at end of file diff --git a/UnitTests/View/Layout/DimTests.cs b/UnitTests/View/Layout/DimTests.cs index dbb9b62ba..1b8019372 100644 --- a/UnitTests/View/Layout/DimTests.cs +++ b/UnitTests/View/Layout/DimTests.cs @@ -1,744 +1,743 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Globalization; -using System.IO; -using System.Linq; using System.Threading; -using Terminal.Gui; using Xunit; using Xunit.Abstractions; // Alias Console to MockConsole so we don't accidentally use Console using Console = Terminal.Gui.FakeConsole; -namespace Terminal.Gui.ViewTests { - public class LayoutTests_DimTests { - readonly ITestOutputHelper output; +namespace Terminal.Gui.ViewTests; - public LayoutTests_DimTests (ITestOutputHelper output) - { - this.output = output; - Console.OutputEncoding = System.Text.Encoding.Default; - // Change current culture - var culture = CultureInfo.CreateSpecificCulture ("en-US"); - Thread.CurrentThread.CurrentCulture = culture; - Thread.CurrentThread.CurrentUICulture = culture; - } +public class DimTests { + readonly ITestOutputHelper _output; - [Fact] - public void New_Works () - { - var dim = new Dim (); - Assert.Equal ("Terminal.Gui.Dim", dim.ToString ()); - } + public DimTests (ITestOutputHelper output) + { + _output = output; + Console.OutputEncoding = System.Text.Encoding.Default; + // Change current culture + var culture = CultureInfo.CreateSpecificCulture ("en-US"); + Thread.CurrentThread.CurrentCulture = culture; + Thread.CurrentThread.CurrentUICulture = culture; + } - [Fact] - public void Sized_SetsValue () - { - var dim = Dim.Sized (0); - Assert.Equal ("Absolute(0)", dim.ToString ()); + [Fact] + public void New_Works () + { + var dim = new Dim (); + Assert.Equal ("Terminal.Gui.Dim", dim.ToString ()); + } - int testVal = 5; - dim = Dim.Sized (testVal); - Assert.Equal ($"Absolute({testVal})", dim.ToString ()); + [Fact] + public void Sized_SetsValue () + { + var dim = Dim.Sized (0); + Assert.Equal ("Absolute(0)", dim.ToString ()); - testVal = -1; - dim = Dim.Sized (testVal); - Assert.Equal ($"Absolute({testVal})", dim.ToString ()); - } + int testVal = 5; + dim = Dim.Sized (testVal); + Assert.Equal ($"Absolute({testVal})", dim.ToString ()); - [Fact] - public void Sized_Equals () - { - int n1 = 0; - int n2 = 0; - var dim1 = Dim.Sized (n1); - var dim2 = Dim.Sized (n2); - Assert.Equal (dim1, dim2); + testVal = -1; + dim = Dim.Sized (testVal); + Assert.Equal ($"Absolute({testVal})", dim.ToString ()); + } - n1 = n2 = 1; - dim1 = Dim.Sized (n1); - dim2 = Dim.Sized (n2); - Assert.Equal (dim1, dim2); + [Fact] + public void Sized_Equals () + { + int n1 = 0; + int n2 = 0; + var dim1 = Dim.Sized (n1); + var dim2 = Dim.Sized (n2); + Assert.Equal (dim1, dim2); - n1 = n2 = -1; - dim1 = Dim.Sized (n1); - dim2 = Dim.Sized (n2); - Assert.Equal (dim1, dim2); + n1 = n2 = 1; + dim1 = Dim.Sized (n1); + dim2 = Dim.Sized (n2); + Assert.Equal (dim1, dim2); - n1 = 0; - n2 = 1; - dim1 = Dim.Sized (n1); - dim2 = Dim.Sized (n2); - Assert.NotEqual (dim1, dim2); - } + n1 = n2 = -1; + dim1 = Dim.Sized (n1); + dim2 = Dim.Sized (n2); + Assert.Equal (dim1, dim2); - [Fact] - public void Width_Set_To_Null_Throws () - { - var dim = Dim.Width (null); - Assert.Throws (() => dim.ToString ()); - } + n1 = 0; + n2 = 1; + dim1 = Dim.Sized (n1); + dim2 = Dim.Sized (n2); + Assert.NotEqual (dim1, dim2); + } - [Fact, TestRespondersDisposed] - public void SetsValue () - { - var testVal = Rect.Empty; - var testValView = new View (testVal); - var dim = Dim.Width (testValView); - Assert.Equal ($"View(Width,View()({testVal}))", dim.ToString ()); - testValView.Dispose (); - - testVal = new Rect (1, 2, 3, 4); - testValView = new View (testVal); - dim = Dim.Width (testValView); - Assert.Equal ($"View(Width,View()({testVal}))", dim.ToString ()); - testValView.Dispose (); + [Fact] + public void Width_Set_To_Null_Throws () + { + var dim = Dim.Width (null); + Assert.Throws (() => dim.ToString ()); + } - } + [Fact] [TestRespondersDisposed] + public void SetsValue () + { + var testVal = Rect.Empty; + var testValView = new View (testVal); + var dim = Dim.Width (testValView); + Assert.Equal ($"View(Width,View()({testVal}))", dim.ToString ()); + testValView.Dispose (); - [Fact, TestRespondersDisposed] - public void Width_Equals () - { - var testRect1 = Rect.Empty; - var view1 = new View (testRect1); - var testRect2 = Rect.Empty; - var view2 = new View (testRect2); + testVal = new Rect (1, 2, 3, 4); + testValView = new View (testVal); + dim = Dim.Width (testValView); + Assert.Equal ($"View(Width,View()({testVal}))", dim.ToString ()); + testValView.Dispose (); - var dim1 = Dim.Width (view1); - var dim2 = Dim.Width (view1); - // FIXED: Dim.Width should support Equals() and this should change to Equal. - Assert.Equal (dim1, dim2); + } - dim2 = Dim.Width (view2); - Assert.NotEqual (dim1, dim2); + [Fact] [TestRespondersDisposed] + public void Width_Equals () + { + var testRect1 = Rect.Empty; + var view1 = new View (testRect1); + var testRect2 = Rect.Empty; + var view2 = new View (testRect2); - testRect1 = new Rect (0, 1, 2, 3); - view1 = new View (testRect1); - testRect2 = new Rect (0, 1, 2, 3); - dim1 = Dim.Width (view1); - dim2 = Dim.Width (view1); - // FIXED: Dim.Width should support Equals() and this should change to Equal. - Assert.Equal (dim1, dim2); + var dim1 = Dim.Width (view1); + var dim2 = Dim.Width (view1); + // FIXED: Dim.Width should support Equals() and this should change to Equal. + Assert.Equal (dim1, dim2); - testRect1 = new Rect (0, -1, 2, 3); - view1 = new View (testRect1); - testRect2 = new Rect (0, -1, 2, 3); - dim1 = Dim.Width (view1); - dim2 = Dim.Width (view1); - // FIXED: Dim.Width should support Equals() and this should change to Equal. - Assert.Equal (dim1, dim2); + dim2 = Dim.Width (view2); + Assert.NotEqual (dim1, dim2); - testRect1 = new Rect (0, -1, 2, 3); - view1 = new View (testRect1); - testRect2 = Rect.Empty; - view2 = new View (testRect2); - dim1 = Dim.Width (view1); - dim2 = Dim.Width (view2); - Assert.NotEqual (dim1, dim2); + testRect1 = new Rect (0, 1, 2, 3); + view1 = new View (testRect1); + testRect2 = new Rect (0, 1, 2, 3); + dim1 = Dim.Width (view1); + dim2 = Dim.Width (view1); + // FIXED: Dim.Width should support Equals() and this should change to Equal. + Assert.Equal (dim1, dim2); + + testRect1 = new Rect (0, -1, 2, 3); + view1 = new View (testRect1); + testRect2 = new Rect (0, -1, 2, 3); + dim1 = Dim.Width (view1); + dim2 = Dim.Width (view1); + // FIXED: Dim.Width should support Equals() and this should change to Equal. + Assert.Equal (dim1, dim2); + + testRect1 = new Rect (0, -1, 2, 3); + view1 = new View (testRect1); + testRect2 = Rect.Empty; + view2 = new View (testRect2); + dim1 = Dim.Width (view1); + dim2 = Dim.Width (view2); + Assert.NotEqual (dim1, dim2); #if DEBUG_IDISPOSABLE - // HACK: Force clean up of Responders to avoid having to Dispose all the Views created above. - Responder.Instances.Clear (); - Assert.Empty (Responder.Instances); + // HACK: Force clean up of Responders to avoid having to Dispose all the Views created above. + Responder.Instances.Clear (); + Assert.Empty (Responder.Instances); #endif - } - - [Fact] - public void Height_Set_To_Null_Throws () - { - var dim = Dim.Height (null); - Assert.Throws (() => dim.ToString ()); - } - - [Fact, TestRespondersDisposed] - public void Height_SetsValue () - { - var testVal = Rect.Empty; - var testValview = new View (testVal); - var dim = Dim.Height (testValview); - Assert.Equal ($"View(Height,View()({testVal}))", dim.ToString ()); - testValview.Dispose (); - - testVal = new Rect (1, 2, 3, 4); - testValview = new View (testVal); - dim = Dim.Height (testValview); - Assert.Equal ($"View(Height,View()({testVal}))", dim.ToString ()); - testValview.Dispose (); - } - - // TODO: Other Dim.Height tests (e.g. Equal?) - - [Fact] - public void Fill_SetsValue () - { - var testMargin = 0; - var dim = Dim.Fill (); - Assert.Equal ($"Fill({testMargin})", dim.ToString ()); - - testMargin = 0; - dim = Dim.Fill (testMargin); - Assert.Equal ($"Fill({testMargin})", dim.ToString ()); - - testMargin = 5; - dim = Dim.Fill (testMargin); - Assert.Equal ($"Fill({testMargin})", dim.ToString ()); - } - - [Fact] - public void Fill_Equal () - { - var margin1 = 0; - var margin2 = 0; - var dim1 = Dim.Fill (margin1); - var dim2 = Dim.Fill (margin2); - Assert.Equal (dim1, dim2); - } - - [Fact] - public void Percent_SetsValue () - { - float f = 0; - var dim = Dim.Percent (f); - Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ()); - f = 0.5F; - dim = Dim.Percent (f); - Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ()); - f = 100; - dim = Dim.Percent (f); - Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ()); - } - - [Fact] - public void Percent_Equals () - { - float n1 = 0; - float n2 = 0; - var dim1 = Dim.Percent (n1); - var dim2 = Dim.Percent (n2); - Assert.Equal (dim1, dim2); - - n1 = n2 = 1; - dim1 = Dim.Percent (n1); - dim2 = Dim.Percent (n2); - Assert.Equal (dim1, dim2); - - n1 = n2 = 0.5f; - dim1 = Dim.Percent (n1); - dim2 = Dim.Percent (n2); - Assert.Equal (dim1, dim2); - - n1 = n2 = 100f; - dim1 = Dim.Percent (n1); - dim2 = Dim.Percent (n2); - Assert.Equal (dim1, dim2); - - n1 = n2 = 0.3f; - dim1 = Dim.Percent (n1, true); - dim2 = Dim.Percent (n2, true); - Assert.Equal (dim1, dim2); - - n1 = n2 = 0.3f; - dim1 = Dim.Percent (n1); - dim2 = Dim.Percent (n2, true); - Assert.NotEqual (dim1, dim2); - - n1 = 0; - n2 = 1; - dim1 = Dim.Percent (n1); - dim2 = Dim.Percent (n2); - Assert.NotEqual (dim1, dim2); - - n1 = 0.5f; - n2 = 1.5f; - dim1 = Dim.Percent (n1); - dim2 = Dim.Percent (n2); - Assert.NotEqual (dim1, dim2); - } - - [Fact] - public void Percent_Invalid_Throws () - { - var dim = Dim.Percent (0); - Assert.Throws (() => dim = Dim.Percent (-1)); - Assert.Throws (() => dim = Dim.Percent (101)); - Assert.Throws (() => dim = Dim.Percent (100.0001F)); - Assert.Throws (() => dim = Dim.Percent (1000001)); - } - - [Fact, AutoInitShutdown] - public void ForceValidatePosDim_True_Dim_Validation_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_Throws () - { - var t = Application.Top; - - var w = new Window () { - Width = Dim.Fill (0), - Height = Dim.Sized (10) - }; - var v = new View ("v") { - Width = Dim.Width (w) - 2, - Height = Dim.Percent (10), - ValidatePosDim = true - }; - - w.Add (v); - t.Add (w); - - t.Ready += (s, e) => { - Assert.Equal (2, w.Width = 2); - Assert.Equal (2, w.Height = 2); - Assert.Throws (() => v.Width = 2); - Assert.Throws (() => v.Height = 2); - v.ValidatePosDim = false; - var exception = Record.Exception (() => v.Width = 2); - Assert.Null (exception); - Assert.Equal (2, v.Width); - exception = Record.Exception (() => v.Height = 2); - Assert.Null (exception); - Assert.Equal (2, v.Height); - }; - - Application.Iteration += (s, a) => Application.RequestStop (); - - Application.Run (); - } - - [Fact, TestRespondersDisposed] - public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Null () - { - var t = new View ("top") { Width = 80, Height = 25 }; - - var w = new Window (new Rect (1, 2, 4, 5)) { Title = "w" }; - t.Add (w); - t.LayoutSubviews (); - - Assert.Equal (3, w.Width = 3); - Assert.Equal (4, w.Height = 4); - t.Dispose (); - } - - [Fact, TestRespondersDisposed] - public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute () - { - var t = new View ("top") { Width = 80, Height = 25 }; - - var w = new Window () { - Width = Dim.Fill (0), - Height = Dim.Sized (10) - }; - var v = new View ("v") { - Width = Dim.Width (w) - 2, - Height = Dim.Percent (10) - }; - - w.Add (v); - t.Add (w); - - t.LayoutSubviews (); - Assert.Equal (2, v.Width = 2); - Assert.Equal (2, v.Height = 2); - - v.LayoutStyle = LayoutStyle.Absolute; - t.LayoutSubviews (); - - Assert.Equal (2, v.Width = 2); - Assert.Equal (2, v.Height = 2); - t.Dispose (); - } - - [Fact, AutoInitShutdown] - public void Only_DimAbsolute_And_DimFactor_As_A_Different_Procedure_For_Assigning_Value_To_Width_Or_Height () - { - // Testing with the Button because it properly handles the Dim class. - var t = Application.Top; - - var w = new Window () { - Width = 100, - Height = 100 - }; - - var f1 = new FrameView ("f1") { - X = 0, - Y = 0, - Width = Dim.Percent (50), - Height = 5 - }; - - var f2 = new FrameView ("f2") { - X = Pos.Right (f1), - Y = 0, - Width = Dim.Fill (), - Height = 5 - }; - - var v1 = new Button ("v1") { - AutoSize = false, - X = Pos.X (f1) + 2, - Y = Pos.Bottom (f1) + 2, - Width = Dim.Width (f1) - 2, - Height = Dim.Fill () - 2, - ValidatePosDim = true - }; - - var v2 = new Button ("v2") { - AutoSize = false, - X = Pos.X (f2) + 2, - Y = Pos.Bottom (f2) + 2, - Width = Dim.Width (f2) - 2, - Height = Dim.Fill () - 2, - ValidatePosDim = true - }; - - var v3 = new Button ("v3") { - AutoSize = false, - Width = Dim.Percent (10), - Height = Dim.Percent (10), - ValidatePosDim = true - }; - - var v4 = new Button ("v4") { - AutoSize = false, - Width = Dim.Sized (50), - Height = Dim.Sized (50), - ValidatePosDim = true - }; - - var v5 = new Button ("v5") { - AutoSize = false, - Width = Dim.Width (v1) - Dim.Width (v3), - Height = Dim.Height (v1) - Dim.Height (v3), - ValidatePosDim = true - }; - - var v6 = new Button ("v6") { - AutoSize = false, - X = Pos.X (f2), - Y = Pos.Bottom (f2) + 2, - Width = Dim.Percent (20, true), - Height = Dim.Percent (20, true), - ValidatePosDim = true - }; - - w.Add (f1, f2, v1, v2, v3, v4, v5, v6); - t.Add (w); - - 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 ("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 ("Combine(View(Width,FrameView(f1)((0,0,49,5)))-Absolute(2))", v1.Width.ToString ()); - 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 - - Assert.Equal ("Combine(View(Width,FrameView(f2)((49,0,49,5)))-Absolute(2))", v2.Width.ToString ()); - Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ()); - Assert.Equal (47, v2.Frame.Width); // 49-2=47 - Assert.Equal (89, v2.Frame.Height); // 98-5-2-2=89 - - Assert.Equal ("Factor(0.1,False)", v3.Width.ToString ()); - Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ()); - Assert.Equal (9, v3.Frame.Width); // 98*10%=9 - Assert.Equal (9, v3.Frame.Height); // 98*10%=9 - - Assert.Equal ("Absolute(50)", v4.Width.ToString ()); - Assert.Equal ("Absolute(50)", v4.Height.ToString ()); - Assert.Equal (50, v4.Frame.Width); - Assert.Equal (50, v4.Frame.Height); - - Assert.Equal ("Combine(View(Width,Button(v1)((2,7,47,89)))-View(Width,Button(v3)((0,0,9,9))))", v5.Width.ToString ()); - Assert.Equal ("Combine(View(Height,Button(v1)((2,7,47,89)))-View(Height,Button(v3)((0,0,9,9))))", v5.Height.ToString ()); - Assert.Equal (38, v5.Frame.Width); // 47-9=38 - Assert.Equal (80, v5.Frame.Height); // 89-9=80 - - Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ()); - Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ()); - Assert.Equal (9, v6.Frame.Width); // 47*20%=9 - Assert.Equal (18, v6.Frame.Height); // 89*20%=18 - - w.Width = 200; - Assert.True (t.LayoutNeeded); - w.Height = 200; - t.LayoutSubviews (); - - Assert.Equal ("Absolute(200)", w.Width.ToString ()); - Assert.Equal ("Absolute(200)", w.Height.ToString ()); - Assert.Equal (200, w.Frame.Width); - Assert.Equal (200, w.Frame.Height); - - f1.Text = "Frame1"; - Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ()); - Assert.Equal ("Absolute(5)", f1.Height.ToString ()); - Assert.Equal (99, f1.Frame.Width); // 100-1=99 - Assert.Equal (5, f1.Frame.Height); - - f2.Text = "Frame2"; - Assert.Equal ("Fill(0)", f2.Width.ToString ()); - Assert.Equal ("Absolute(5)", f2.Height.ToString ()); - Assert.Equal (99, f2.Frame.Width); // 100-1=99 - Assert.Equal (5, f2.Frame.Height); - - v1.Text = "Button1"; - Assert.Equal ("Combine(View(Width,FrameView(f1)((0,0,99,5)))-Absolute(2))", v1.Width.ToString ()); - 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"; - Assert.Equal ("Combine(View(Width,FrameView(f2)((99,0,99,5)))-Absolute(2))", v2.Width.ToString ()); - 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 - - v3.Text = "Button3"; - Assert.Equal ("Factor(0.1,False)", v3.Width.ToString ()); - Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ()); - Assert.Equal (19, v3.Frame.Width); // 198*10%=19 * Percent is related to the super-view if it isn't null otherwise the view width - Assert.Equal (19, v3.Frame.Height); // 199*10%=19 - - v4.Text = "Button4"; - v4.AutoSize = false; - Assert.Equal ("Absolute(50)", v4.Width.ToString ()); - Assert.Equal ("Absolute(50)", v4.Height.ToString ()); - 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 (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"; - Assert.Equal ("Combine(View(Width,Button(v1)((2,7,97,189)))-View(Width,Button(v3)((0,0,19,19))))", v5.Width.ToString ()); - Assert.Equal ("Combine(View(Height,Button(v1)((2,7,97,189)))-View(Height,Button(v3)((0,0,19,19))))", v5.Height.ToString ()); - Assert.Equal (78, v5.Frame.Width); // 97-9=78 - Assert.Equal (170, v5.Frame.Height); // 189-19=170 - - v6.Text = "Button6"; - Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ()); - Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ()); - Assert.Equal (19, v6.Frame.Width); // 99*20%=19 - Assert.Equal (38, v6.Frame.Height); // 198-7*20=18 - }; - - Application.Iteration += (s, a) => Application.RequestStop (); - - Application.Run (); - } - - // See #2461 - //[Fact] - //public void Dim_Referencing_SuperView_Throws () - //{ - // var super = new View ("super") { - // Width = 10, - // Height = 10 - // }; - // var view = new View ("view") { - // Width = Dim.Width (super), // this is not allowed - // Height = Dim.Height (super), // this is not allowed - // }; - - // super.Add (view); - // super.BeginInit (); - // super.EndInit (); - // Assert.Throws (() => super.LayoutSubviews ()); - //} - - /// - /// 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 () - { - var t = new View () { Width = 80, Height = 25 }; - - var w = new Window () { - Width = Dim.Width (t) - 2, // 78 - Height = Dim.Height (t) - 2 // 23 - }; - var f = new FrameView (); - var v1 = new View () { - Width = Dim.Width (w) - 2, // 76 - Height = Dim.Height (w) - 2 // 21 - }; - var v2 = new View () { - Width = Dim.Width (v1) - 2, // 74 - Height = Dim.Height (v1) - 2 // 19 - }; - - f.Add (v1, v2); - w.Add (f); - t.Add (w); - - // BUGBUG: v2 - f references t here; t is f's super-superview. This is supported! - // BUGBUG: v2 - f references v2 here; v2 is f's subview. This is not supported! - f.Width = Dim.Width (t) - Dim.Width (v2); // 80 - 74 = 6 - f.Height = Dim.Height (t) - Dim.Height (v2); // 25 - 19 = 6 - - Assert.Throws (t.LayoutSubviews); - Assert.Equal (80, t.Frame.Width); - Assert.Equal (25, t.Frame.Height); - Assert.Equal (78, w.Frame.Width); - Assert.Equal (23, w.Frame.Height); - // BUGBUG: v2 - this no longer works - see above - //Assert.Equal (6, f.Frame.Width); - //Assert.Equal (6, f.Frame.Height); - //Assert.Equal (76, v1.Frame.Width); - //Assert.Equal (21, v1.Frame.Height); - //Assert.Equal (74, v2.Frame.Width); - //Assert.Equal (19, v2.Frame.Height); - t.Dispose (); - } - - [Fact, TestRespondersDisposed] - public void DimCombine_ObtuseScenario_Does_Not_Throw_If_Two_SubViews_Refs_The_Same_SuperView () - { - var t = new View ("top") { Width = 80, Height = 25 }; - - var w = new Window () { - Width = Dim.Width (t) - 2, // 78 - Height = Dim.Height (t) - 2 // 23 - }; - var f = new FrameView (); - var v1 = new View () { - Width = Dim.Width (w) - 2, // 76 - Height = Dim.Height (w) - 2 // 21 - }; - var v2 = new View () { - Width = Dim.Width (v1) - 2, // 74 - Height = Dim.Height (v1) - 2 // 19 - }; - - f.Add (v1, v2); - w.Add (f); - t.Add (w); - - f.Width = Dim.Width (t) - Dim.Width (w) + 4; // 80 - 74 = 6 - f.Height = Dim.Height (t) - Dim.Height (w) + 4; // 25 - 19 = 6 - - // BUGBUG: v2 - f references t and w here; t is f's super-superview and w is f's superview. This is supported! - var exception = Record.Exception (t.LayoutSubviews); + } + + [Fact] + public void Height_Set_To_Null_Throws () + { + var dim = Dim.Height (null); + Assert.Throws (() => dim.ToString ()); + } + + [Fact] [TestRespondersDisposed] + public void Height_SetsValue () + { + var testVal = Rect.Empty; + var testValview = new View (testVal); + var dim = Dim.Height (testValview); + Assert.Equal ($"View(Height,View()({testVal}))", dim.ToString ()); + testValview.Dispose (); + + testVal = new Rect (1, 2, 3, 4); + testValview = new View (testVal); + dim = Dim.Height (testValview); + Assert.Equal ($"View(Height,View()({testVal}))", dim.ToString ()); + testValview.Dispose (); + } + + // TODO: Other Dim.Height tests (e.g. Equal?) + + [Fact] + public void Fill_SetsValue () + { + int testMargin = 0; + var dim = Dim.Fill (); + Assert.Equal ($"Fill({testMargin})", dim.ToString ()); + + testMargin = 0; + dim = Dim.Fill (testMargin); + Assert.Equal ($"Fill({testMargin})", dim.ToString ()); + + testMargin = 5; + dim = Dim.Fill (testMargin); + Assert.Equal ($"Fill({testMargin})", dim.ToString ()); + } + + [Fact] + public void Fill_Equal () + { + int margin1 = 0; + int margin2 = 0; + var dim1 = Dim.Fill (margin1); + var dim2 = Dim.Fill (margin2); + Assert.Equal (dim1, dim2); + } + + [Fact] + public void Percent_SetsValue () + { + float f = 0; + var dim = Dim.Percent (f); + Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ()); + f = 0.5F; + dim = Dim.Percent (f); + Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ()); + f = 100; + dim = Dim.Percent (f); + Assert.Equal ($"Factor({f / 100:0.###},{false})", dim.ToString ()); + } + + [Fact] + public void Percent_Equals () + { + float n1 = 0; + float n2 = 0; + var dim1 = Dim.Percent (n1); + var dim2 = Dim.Percent (n2); + Assert.Equal (dim1, dim2); + + n1 = n2 = 1; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2); + Assert.Equal (dim1, dim2); + + n1 = n2 = 0.5f; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2); + Assert.Equal (dim1, dim2); + + n1 = n2 = 100f; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2); + Assert.Equal (dim1, dim2); + + n1 = n2 = 0.3f; + dim1 = Dim.Percent (n1, true); + dim2 = Dim.Percent (n2, true); + Assert.Equal (dim1, dim2); + + n1 = n2 = 0.3f; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2, true); + Assert.NotEqual (dim1, dim2); + + n1 = 0; + n2 = 1; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2); + Assert.NotEqual (dim1, dim2); + + n1 = 0.5f; + n2 = 1.5f; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2); + Assert.NotEqual (dim1, dim2); + } + + [Fact] + public void Percent_Invalid_Throws () + { + var dim = Dim.Percent (0); + Assert.Throws (() => dim = Dim.Percent (-1)); + Assert.Throws (() => dim = Dim.Percent (101)); + Assert.Throws (() => dim = Dim.Percent (100.0001F)); + Assert.Throws (() => dim = Dim.Percent (1000001)); + } + + [Fact] [AutoInitShutdown] + public void ForceValidatePosDim_True_Dim_Validation_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_Throws () + { + var t = Application.Top; + + var w = new Window () { + Width = Dim.Fill (0), + Height = Dim.Sized (10) + }; + var v = new View ("v") { + Width = Dim.Width (w) - 2, + Height = Dim.Percent (10), + ValidatePosDim = true + }; + + w.Add (v); + t.Add (w); + + t.Ready += (s, e) => { + Assert.Equal (2, w.Width = 2); + Assert.Equal (2, w.Height = 2); + Assert.Throws (() => v.Width = 2); + Assert.Throws (() => v.Height = 2); + v.ValidatePosDim = false; + var exception = Record.Exception (() => v.Width = 2); Assert.Null (exception); - Assert.Equal (80, t.Frame.Width); - Assert.Equal (25, t.Frame.Height); - Assert.Equal (78, w.Frame.Width); - Assert.Equal (23, w.Frame.Height); - Assert.Equal (6, f.Frame.Width); - Assert.Equal (6, f.Frame.Height); - Assert.Equal (76, v1.Frame.Width); - Assert.Equal (21, v1.Frame.Height); - Assert.Equal (74, v2.Frame.Width); - Assert.Equal (19, v2.Frame.Height); - t.Dispose (); - } + Assert.Equal (2, v.Width); + exception = Record.Exception (() => v.Height = 2); + Assert.Null (exception); + Assert.Equal (2, v.Height); + }; - [Fact, TestRespondersDisposed] - public void PosCombine_View_Not_Added_Throws () - { - var t = new View () { Width = 80, Height = 50 }; + Application.Iteration += (s, a) => Application.RequestStop (); - // BUGBUG: v2 - super should not reference it's superview (t) - var super = new View () { - Width = Dim.Width (t) - 2, - Height = Dim.Height (t) - 2 - }; - t.Add (super); + Application.Run (); + } - var sub = new View (); - super.Add (sub); + [Fact] [TestRespondersDisposed] + public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Null () + { + var t = new View ("top") { Width = 80, Height = 25 }; - 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); + var w = new Window (new Rect (1, 2, 4, 5)) { Title = "w" }; + t.Add (w); + t.LayoutSubviews (); - Assert.Throws (() => t.LayoutSubviews ()); - t.Dispose (); - v2.Dispose (); - } + Assert.Equal (3, w.Width = 3); + Assert.Equal (4, w.Height = 4); + t.Dispose (); + } - [Fact, AutoInitShutdown] - public void Dim_Add_Operator () - { - var top = Application.Top; + [Fact] [TestRespondersDisposed] + public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute () + { + var t = new View ("top") { Width = 80, Height = 25 }; - 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 = 0; + var w = new Window () { + Width = Dim.Fill (0), + Height = Dim.Sized (10) + }; + var v = new View ("v") { + Width = Dim.Width (w) - 2, + Height = Dim.Percent (10) + }; - field.KeyDown += (s, k) => { - if (k.KeyCode == KeyCode.Enter) { - field.Text = $"Label {count}"; - var label = new Label (field.Text) { X = 0, Y = view.Bounds.Height, Width = 20 }; - view.Add (label); - Assert.Equal ($"Label {count}", label.Text); - Assert.Equal ($"Absolute({count})", label.Y.ToString ()); + w.Add (v); + t.Add (w); - Assert.Equal ($"Absolute({count})", view.Height.ToString ()); - view.Height += 1; - count++; - Assert.Equal ($"Absolute({count})", view.Height.ToString ()); - } - }; + t.LayoutSubviews (); + Assert.Equal (2, v.Width = 2); + Assert.Equal (2, v.Height = 2); - Application.Iteration += (s, a) => { - while (count < 20) field.NewKeyDownEvent (new (KeyCode.Enter)); + v.LayoutStyle = LayoutStyle.Absolute; + t.LayoutSubviews (); - Application.RequestStop (); - }; + Assert.Equal (2, v.Width = 2); + Assert.Equal (2, v.Height = 2); + t.Dispose (); + } - var win = new Window (); - win.Add (view); - win.Add (field); + [Fact] [AutoInitShutdown] + public void Only_DimAbsolute_And_DimFactor_As_A_Different_Procedure_For_Assigning_Value_To_Width_Or_Height () + { + // Testing with the Button because it properly handles the Dim class. + var t = Application.Top; - top.Add (win); + var w = new Window () { + Width = 100, + Height = 100 + }; - Application.Run (top); + var f1 = new FrameView ("f1") { + X = 0, + Y = 0, + Width = Dim.Percent (50), + Height = 5 + }; - Assert.Equal (20, count); - } + var f2 = new FrameView ("f2") { + X = Pos.Right (f1), + Y = 0, + Width = Dim.Fill (), + Height = 5 + }; - private string [] expecteds = new string [21] { -@" + var v1 = new Button ("v1") { + AutoSize = false, + X = Pos.X (f1) + 2, + Y = Pos.Bottom (f1) + 2, + Width = Dim.Width (f1) - 2, + Height = Dim.Fill () - 2, + ValidatePosDim = true + }; + + var v2 = new Button ("v2") { + AutoSize = false, + X = Pos.X (f2) + 2, + Y = Pos.Bottom (f2) + 2, + Width = Dim.Width (f2) - 2, + Height = Dim.Fill () - 2, + ValidatePosDim = true + }; + + var v3 = new Button ("v3") { + AutoSize = false, + Width = Dim.Percent (10), + Height = Dim.Percent (10), + ValidatePosDim = true + }; + + var v4 = new Button ("v4") { + AutoSize = false, + Width = Dim.Sized (50), + Height = Dim.Sized (50), + ValidatePosDim = true + }; + + var v5 = new Button ("v5") { + AutoSize = false, + Width = Dim.Width (v1) - Dim.Width (v3), + Height = Dim.Height (v1) - Dim.Height (v3), + ValidatePosDim = true + }; + + var v6 = new Button ("v6") { + AutoSize = false, + X = Pos.X (f2), + Y = Pos.Bottom (f2) + 2, + Width = Dim.Percent (20, true), + Height = Dim.Percent (20, true), + ValidatePosDim = true + }; + + w.Add (f1, f2, v1, v2, v3, v4, v5, v6); + t.Add (w); + + 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 ("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 ("Combine(View(Width,FrameView(f1)((0,0,49,5)))-Absolute(2))", v1.Width.ToString ()); + 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 + + Assert.Equal ("Combine(View(Width,FrameView(f2)((49,0,49,5)))-Absolute(2))", v2.Width.ToString ()); + Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ()); + Assert.Equal (47, v2.Frame.Width); // 49-2=47 + Assert.Equal (89, v2.Frame.Height); // 98-5-2-2=89 + + Assert.Equal ("Factor(0.1,False)", v3.Width.ToString ()); + Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ()); + Assert.Equal (9, v3.Frame.Width); // 98*10%=9 + Assert.Equal (9, v3.Frame.Height); // 98*10%=9 + + Assert.Equal ("Absolute(50)", v4.Width.ToString ()); + Assert.Equal ("Absolute(50)", v4.Height.ToString ()); + Assert.Equal (50, v4.Frame.Width); + Assert.Equal (50, v4.Frame.Height); + + Assert.Equal ("Combine(View(Width,Button(v1)((2,7,47,89)))-View(Width,Button(v3)((0,0,9,9))))", v5.Width.ToString ()); + Assert.Equal ("Combine(View(Height,Button(v1)((2,7,47,89)))-View(Height,Button(v3)((0,0,9,9))))", v5.Height.ToString ()); + Assert.Equal (38, v5.Frame.Width); // 47-9=38 + Assert.Equal (80, v5.Frame.Height); // 89-9=80 + + Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ()); + Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ()); + Assert.Equal (9, v6.Frame.Width); // 47*20%=9 + Assert.Equal (18, v6.Frame.Height); // 89*20%=18 + + w.Width = 200; + Assert.True (t.LayoutNeeded); + w.Height = 200; + t.LayoutSubviews (); + + Assert.Equal ("Absolute(200)", w.Width.ToString ()); + Assert.Equal ("Absolute(200)", w.Height.ToString ()); + Assert.Equal (200, w.Frame.Width); + Assert.Equal (200, w.Frame.Height); + + f1.Text = "Frame1"; + Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ()); + Assert.Equal ("Absolute(5)", f1.Height.ToString ()); + Assert.Equal (99, f1.Frame.Width); // 100-1=99 + Assert.Equal (5, f1.Frame.Height); + + f2.Text = "Frame2"; + Assert.Equal ("Fill(0)", f2.Width.ToString ()); + Assert.Equal ("Absolute(5)", f2.Height.ToString ()); + Assert.Equal (99, f2.Frame.Width); // 100-1=99 + Assert.Equal (5, f2.Frame.Height); + + v1.Text = "Button1"; + Assert.Equal ("Combine(View(Width,FrameView(f1)((0,0,99,5)))-Absolute(2))", v1.Width.ToString ()); + 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"; + Assert.Equal ("Combine(View(Width,FrameView(f2)((99,0,99,5)))-Absolute(2))", v2.Width.ToString ()); + 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 + + v3.Text = "Button3"; + Assert.Equal ("Factor(0.1,False)", v3.Width.ToString ()); + Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ()); + Assert.Equal (19, v3.Frame.Width); // 198*10%=19 * Percent is related to the super-view if it isn't null otherwise the view width + Assert.Equal (19, v3.Frame.Height); // 199*10%=19 + + v4.Text = "Button4"; + v4.AutoSize = false; + Assert.Equal ("Absolute(50)", v4.Width.ToString ()); + Assert.Equal ("Absolute(50)", v4.Height.ToString ()); + 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 (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"; + Assert.Equal ("Combine(View(Width,Button(v1)((2,7,97,189)))-View(Width,Button(v3)((0,0,19,19))))", v5.Width.ToString ()); + Assert.Equal ("Combine(View(Height,Button(v1)((2,7,97,189)))-View(Height,Button(v3)((0,0,19,19))))", v5.Height.ToString ()); + Assert.Equal (78, v5.Frame.Width); // 97-9=78 + Assert.Equal (170, v5.Frame.Height); // 189-19=170 + + v6.Text = "Button6"; + Assert.Equal ("Factor(0.2,True)", v6.Width.ToString ()); + Assert.Equal ("Factor(0.2,True)", v6.Height.ToString ()); + Assert.Equal (19, v6.Frame.Width); // 99*20%=19 + Assert.Equal (38, v6.Frame.Height); // 198-7*20=18 + }; + + Application.Iteration += (s, a) => Application.RequestStop (); + + Application.Run (); + } + + // See #2461 + //[Fact] + //public void Dim_Referencing_SuperView_Throws () + //{ + // var super = new View ("super") { + // Width = 10, + // Height = 10 + // }; + // var view = new View ("view") { + // Width = Dim.Width (super), // this is not allowed + // Height = Dim.Height (super), // this is not allowed + // }; + + // super.Add (view); + // super.BeginInit (); + // super.EndInit (); + // Assert.Throws (() => super.LayoutSubviews ()); + //} + + /// + /// 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 () + { + var t = new View () { Width = 80, Height = 25 }; + + var w = new Window () { + Width = Dim.Width (t) - 2, // 78 + Height = Dim.Height (t) - 2 // 23 + }; + var f = new FrameView (); + var v1 = new View () { + Width = Dim.Width (w) - 2, // 76 + Height = Dim.Height (w) - 2 // 21 + }; + var v2 = new View () { + Width = Dim.Width (v1) - 2, // 74 + Height = Dim.Height (v1) - 2 // 19 + }; + + f.Add (v1, v2); + w.Add (f); + t.Add (w); + + // BUGBUG: v2 - f references t here; t is f's super-superview. This is supported! + // BUGBUG: v2 - f references v2 here; v2 is f's subview. This is not supported! + f.Width = Dim.Width (t) - Dim.Width (v2); // 80 - 74 = 6 + f.Height = Dim.Height (t) - Dim.Height (v2); // 25 - 19 = 6 + + Assert.Throws (t.LayoutSubviews); + Assert.Equal (80, t.Frame.Width); + Assert.Equal (25, t.Frame.Height); + Assert.Equal (78, w.Frame.Width); + Assert.Equal (23, w.Frame.Height); + // BUGBUG: v2 - this no longer works - see above + //Assert.Equal (6, f.Frame.Width); + //Assert.Equal (6, f.Frame.Height); + //Assert.Equal (76, v1.Frame.Width); + //Assert.Equal (21, v1.Frame.Height); + //Assert.Equal (74, v2.Frame.Width); + //Assert.Equal (19, v2.Frame.Height); + t.Dispose (); + } + + [Fact] [TestRespondersDisposed] + public void DimCombine_ObtuseScenario_Does_Not_Throw_If_Two_SubViews_Refs_The_Same_SuperView () + { + var t = new View ("top") { Width = 80, Height = 25 }; + + var w = new Window () { + Width = Dim.Width (t) - 2, // 78 + Height = Dim.Height (t) - 2 // 23 + }; + var f = new FrameView (); + var v1 = new View () { + Width = Dim.Width (w) - 2, // 76 + Height = Dim.Height (w) - 2 // 21 + }; + var v2 = new View () { + Width = Dim.Width (v1) - 2, // 74 + Height = Dim.Height (v1) - 2 // 19 + }; + + f.Add (v1, v2); + w.Add (f); + t.Add (w); + + f.Width = Dim.Width (t) - Dim.Width (w) + 4; // 80 - 74 = 6 + f.Height = Dim.Height (t) - Dim.Height (w) + 4; // 25 - 19 = 6 + + // BUGBUG: v2 - f references t and w here; t is f's super-superview and w is f's superview. This is supported! + var exception = Record.Exception (t.LayoutSubviews); + Assert.Null (exception); + Assert.Equal (80, t.Frame.Width); + Assert.Equal (25, t.Frame.Height); + Assert.Equal (78, w.Frame.Width); + Assert.Equal (23, w.Frame.Height); + Assert.Equal (6, f.Frame.Width); + Assert.Equal (6, f.Frame.Height); + Assert.Equal (76, v1.Frame.Width); + Assert.Equal (21, v1.Frame.Height); + Assert.Equal (74, v2.Frame.Width); + Assert.Equal (19, v2.Frame.Height); + t.Dispose (); + } + + [Fact] [TestRespondersDisposed] + public void PosCombine_View_Not_Added_Throws () + { + var t = new View () { Width = 80, Height = 50 }; + + // BUGBUG: v2 - super should not reference it's superview (t) + var super = new View () { + Width = Dim.Width (t) - 2, + Height = Dim.Height (t) - 2 + }; + t.Add (super); + + var sub = new View (); + super.Add (sub); + + 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); + + Assert.Throws (() => t.LayoutSubviews ()); + t.Dispose (); + v2.Dispose (); + } + + [Fact] [AutoInitShutdown] + public void Dim_Add_Operator () + { + var top = Application.Top; + + var view = new View () { X = 0, Y = 0, Width = 20, Height = 0 }; + var field = new TextField () { X = 0, Y = Pos.Bottom (view), Width = 20 }; + int count = 0; + + field.KeyDown += (s, k) => { + if (k.KeyCode == KeyCode.Enter) { + field.Text = $"Label {count}"; + var label = new Label (field.Text) { X = 0, Y = view.Bounds.Height, Width = 20 }; + view.Add (label); + Assert.Equal ($"Label {count}", label.Text); + Assert.Equal ($"Absolute({count})", label.Y.ToString ()); + + Assert.Equal ($"Absolute({count})", view.Height.ToString ()); + view.Height += 1; + count++; + Assert.Equal ($"Absolute({count})", view.Height.ToString ()); + } + }; + + Application.Iteration += (s, a) => { + while (count < 20) { + field.NewKeyDownEvent (new Key (KeyCode.Enter)); + } + + Application.RequestStop (); + }; + + var win = new Window (); + win.Add (view); + win.Add (field); + + top.Add (win); + + Application.Run (top); + + Assert.Equal (20, count); + } + + string [] expecteds = new string [21] { + @" ┌────────────────────┐ │View with long text │ │ │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ │Label 0 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ │Label 1 │ │Label 1 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -746,7 +745,7 @@ namespace Terminal.Gui.ViewTests { │Label 2 │ │Label 2 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -755,7 +754,7 @@ namespace Terminal.Gui.ViewTests { │Label 3 │ │Label 3 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -765,7 +764,7 @@ namespace Terminal.Gui.ViewTests { │Label 4 │ │Label 4 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -776,7 +775,7 @@ namespace Terminal.Gui.ViewTests { │Label 5 │ │Label 5 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -788,7 +787,7 @@ namespace Terminal.Gui.ViewTests { │Label 6 │ │Label 6 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -801,7 +800,7 @@ namespace Terminal.Gui.ViewTests { │Label 7 │ │Label 7 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -815,7 +814,7 @@ namespace Terminal.Gui.ViewTests { │Label 8 │ │Label 8 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -830,7 +829,7 @@ namespace Terminal.Gui.ViewTests { │Label 9 │ │Label 9 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -846,7 +845,7 @@ namespace Terminal.Gui.ViewTests { │Label 10 │ │Label 10 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -863,7 +862,7 @@ namespace Terminal.Gui.ViewTests { │Label 11 │ │Label 11 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -881,7 +880,7 @@ namespace Terminal.Gui.ViewTests { │Label 12 │ │Label 12 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -900,7 +899,7 @@ namespace Terminal.Gui.ViewTests { │Label 13 │ │Label 13 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -920,7 +919,7 @@ namespace Terminal.Gui.ViewTests { │Label 14 │ │Label 14 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -941,7 +940,7 @@ namespace Terminal.Gui.ViewTests { │Label 15 │ │Label 15 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -963,7 +962,7 @@ namespace Terminal.Gui.ViewTests { │Label 16 │ │Label 16 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -986,7 +985,7 @@ namespace Terminal.Gui.ViewTests { │Label 17 │ │Label 17 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -1010,7 +1009,7 @@ namespace Terminal.Gui.ViewTests { │Label 18 │ │Label 18 │ └────────────────────┘", -@" + @" ┌────────────────────┐ │View with long text │ │Label 0 │ @@ -1034,344 +1033,346 @@ namespace Terminal.Gui.ViewTests { │Label 18 │ │Label 19 │ │Label 19 │ -└────────────────────┘", -}; +└────────────────────┘" + }; - [Fact, AutoInitShutdown] - public void Dim_Add_Operator_With_Text () - { - var top = Application.Top; + [Fact] [AutoInitShutdown] + public void Dim_Add_Operator_With_Text () + { + var top = Application.Top; - // BUGBUG: v2 - If a View's height is zero, it should not be drawn. - //// Although view height is zero the text it's draw due the SetMinWidthHeight method - var view = new View ("View with long text") { X = 0, Y = 0, Width = 20, Height = 1 }; - var field = new TextField () { X = 0, Y = Pos.Bottom (view), Width = 20 }; - var count = 0; - var listLabels = new List