From f48ff99628ca8159bcf3bd79337d1f9b4bdb0dd6 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Sat, 23 Dec 2023 10:23:00 -0700 Subject: [PATCH] Progress on low level unit tess --- Terminal.Gui/View/Layout/PosDim.cs | 6 +- Terminal.Gui/View/Layout/ViewLayout.cs | 28 +-- UICatalog/Scenarios/ComputedLayout.cs | 13 ++ UICatalog/Scenarios/DimAutoDemo.cs | 24 +- .../View/Layout/SetRelativeLayoutTests.cs | 214 ++++++++++++++++-- UnitTests/View/ViewTests.cs | 14 +- 6 files changed, 242 insertions(+), 57 deletions(-) diff --git a/Terminal.Gui/View/Layout/PosDim.cs b/Terminal.Gui/View/Layout/PosDim.cs index 3e308d6fc..1971ee863 100644 --- a/Terminal.Gui/View/Layout/PosDim.cs +++ b/Terminal.Gui/View/Layout/PosDim.cs @@ -62,7 +62,7 @@ namespace Terminal.Gui; /// /// /// -/// +/// /// /// Creates a object that tracks the Left (X) position of the specified . /// @@ -450,7 +450,7 @@ public class Pos { /// /// /// -/// +/// /// /// Creates a object that automatically sizes the view to fit all of the view's SubViews. /// @@ -596,7 +596,7 @@ public class Dim { /// The AutoSize object. /// Specifies how will compute the dimension. The default is . NOT CURRENTLY SUPPORTED. /// Specifies the minimum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED. - /// Specifies the maximum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED. + /// Specifies the maximum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED. public static Dim Auto (DimAutoStyle style = DimAutoStyle.Subviews, Dim min = null, Dim max = null) { if (style == DimAutoStyle.Text) { diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs index decd732dd..a390d3c49 100644 --- a/Terminal.Gui/View/Layout/ViewLayout.cs +++ b/Terminal.Gui/View/Layout/ViewLayout.cs @@ -697,7 +697,7 @@ public partial class View { // dimension: the current dimension (width or height) // autosize: the size to use if autosize = true // This mehod is recursive if d is Dim.DimCombine - int GetNewDimension (Dim d, int location, int dimension, int autosize) + int GetNewDimension (Dim d, int location, int dimension, int autosize) { int newDimension; switch (d) { @@ -724,9 +724,9 @@ public partial class View { case Dim.DimAuto auto: var thickness = GetFramesThickness (); - newDimension = GetNewDimension (auto._min, location, dimension, autosize); + //newDimension = GetNewDimension (auto._min, location, dimension, autosize); if (width) { - int furthestRight = Subviews.Count == 0 ? 0 : Subviews.Max (v => v.Frame.X + v.Frame.Width); + int furthestRight = Subviews.Count == 0 ? 0 : Subviews.Where (v => v.X is not Pos.PosAnchorEnd).Max (v => v.Frame.X + v.Frame.Width); //Debug.Assert(superviewBounds.Width == (SuperView?.Bounds.Width ?? 0)); newDimension = int.Max (furthestRight + thickness.Left + thickness.Right, auto._min?.Anchor (superviewBounds.Width) ?? 0); } else { @@ -752,20 +752,11 @@ public partial class View { // Determine new location switch (pos) { case Pos.PosCenter posCenter: - if (dim == null) { - // BUGBUG: In what situation is dim == null here? None that I can find. - // dim == null is the same as dim == Dim.FIll (0) - throw new ArgumentException (); - newDimension = AutoSize ? autosizeDimension : superviewDimension; - newLocation = posCenter.Anchor (superviewDimension - newDimension); - } else { - //newLocation = posCenter?.Anchor (superviewDimension) ?? 0; - //newDimension = Math.Max (GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension), 0); - - newDimension = posCenter.Anchor (superviewDimension); - newDimension = AutoSize && autosizeDimension > newDimension ? autosizeDimension : newDimension; - newLocation = posCenter.Anchor (superviewDimension - newDimension); - } + // For Center, the dimension is dependent on location, but we need to force getting the dimension first + // using a location of 0 + newDimension = Math.Max (GetNewDimension (dim, 0, superviewDimension, autosizeDimension), 0); + newLocation = posCenter.Anchor (superviewDimension - newDimension); + newDimension = Math.Max (GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension), 0); break; case Pos.PosCombine combine: @@ -786,7 +777,7 @@ public partial class View { case Pos.PosFactor: case Pos.PosFunc: case Pos.PosView: - default: + default: newLocation = pos?.Anchor (superviewDimension) ?? 0; newDimension = Math.Max (GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension), 0); break; @@ -1046,6 +1037,7 @@ public partial class View { if (v.Width is Dim.DimAuto || v.Height is Dim.DimAuto) { // If the view is auto-sized... var f = v.Frame; + v._frame = new Rect (v.Frame.X, v.Frame.Y, 0, 0); LayoutSubview (v, new Rect (GetBoundsOffset (), Bounds.Size)); if (v.Frame != f) { // The subviews changed; do it again diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs index 48136db6e..19a9fde2c 100644 --- a/UICatalog/Scenarios/ComputedLayout.cs +++ b/UICatalog/Scenarios/ComputedLayout.cs @@ -163,6 +163,19 @@ namespace UICatalog.Scenarios { }; Application.Top.Add (oddballButton); + oddballButton = new Button ("Center - 1") { + X = Pos.Center () - 1, + Y = Pos.Bottom (oddballButton) + }; + Application.Top.Add (oddballButton); + + // Won't be visible: + //oddballButton = new Button ("1 - Center") { + // X = 1 - Pos.Center (), + // Y = Pos.Bottom (oddballButton) + //}; + //Application.Top.Add (oddballButton); + // This demonstrates nonsense: it the same as using Pos.AnchorEnd (100/2=50 + 100/2=50 = 100 - 50) // The `- Pos.Percent(5)` is there so at least something is visible oddballButton = new Button ("Center + Center - Percent(50)") { diff --git a/UICatalog/Scenarios/DimAutoDemo.cs b/UICatalog/Scenarios/DimAutoDemo.cs index 685447c72..b66cd6587 100644 --- a/UICatalog/Scenarios/DimAutoDemo.cs +++ b/UICatalog/Scenarios/DimAutoDemo.cs @@ -30,9 +30,9 @@ public class DimAutoDemo : Scenario { var resetButton = new Button () { Text = "P_ut Button Back", - X = Pos.Center (), Y = Pos.Bottom (label) }; + resetButton.X = Pos.AnchorEnd () - 19; var movingButton = new Button () { Text = "Press to make button move down.", @@ -44,9 +44,6 @@ public class DimAutoDemo : Scenario { movingButton.Y = movingButton.Frame.Y + 1; }; - resetButton.Clicked += (s, e) => { - movingButton.Y = Pos.Bottom (resetButton); - }; var view = new FrameView () { Title = "Type in the TextField to make View grow.", @@ -58,6 +55,10 @@ public class DimAutoDemo : Scenario { view.ValidatePosDim = true; view.Add (textField, label, resetButton, movingButton); + resetButton.Clicked += (s, e) => { + movingButton.Y = Pos.Bottom (resetButton); + }; + var dlgButton = new Button () { Text = "Open Test _Dialog", X = Pos.Right (view), @@ -82,22 +83,31 @@ public class DimAutoDemo : Scenario { //cancel.Clicked += (s, _) => Application.RequestStop (dlg); //dlg.AddButton (cancel); - var label = new Label ("This is a label (AutoSize = false; Dim.Auto(3/20). Press Esc to close.") { + var label = new Label ("This is a label (AutoSize = false; Dim.Auto(3/20). Press Esc to close. Even more text.") { AutoSize = false, - X = Pos.Center(), + X = Pos.Center (), Y = 0, Height = Dim.Auto (min: 3), Width = Dim.Auto (min: 20), ColorScheme = Colors.Menu }; + var text = new TextField () { + Text = "TextField... X = 1; Y = Pos.Bottom (label), Width = Dim.Fill (1); Height = Dim.Fill(1)", + TextFormatter = new TextFormatter () { WordWrap = true }, + X = 20, + Y = Pos.Bottom (label), + Width = Dim.Fill (20), + Height = Dim.Fill (10) + }; var btn = new Button ("AnchorEnd") { Y = Pos.AnchorEnd (1) }; // TODO: We should really fix AnchorEnd to do this automatically. btn.X = Pos.AnchorEnd () - (Pos.Right (btn) - Pos.Left (btn)); - dlg.Add (btn); dlg.Add (label); + dlg.Add (text); + dlg.Add (btn); Application.Run (dlg); } } \ No newline at end of file diff --git a/UnitTests/View/Layout/SetRelativeLayoutTests.cs b/UnitTests/View/Layout/SetRelativeLayoutTests.cs index b389383d2..1f0ef550d 100644 --- a/UnitTests/View/Layout/SetRelativeLayoutTests.cs +++ b/UnitTests/View/Layout/SetRelativeLayoutTests.cs @@ -20,18 +20,18 @@ public class SetRelativeLayoutTests { }; // Default layout style is Computed - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.Null (view.X); Assert.Null (view.Y); view.BeginInit(); view.EndInit(); - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.Null (view.X); Assert.Null (view.Y); - view.SetRelativeLayout(new Rect(5, 5, 10, 10)); - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + view.SetRelativeLayout (new Rect (5, 5, 10, 10)); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.Null (view.X); Assert.Null (view.Y); @@ -50,18 +50,18 @@ public class SetRelativeLayoutTests { }; // Default layout style is Computed - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.NotNull (view.X); Assert.NotNull (view.Y); view.BeginInit (); view.EndInit (); - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.NotNull (view.X); Assert.NotNull (view.Y); view.SetRelativeLayout (new Rect (5, 5, 10, 10)); - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.NotNull (view.X); Assert.NotNull (view.Y); @@ -78,17 +78,17 @@ public class SetRelativeLayoutTests { }; // Default layout style is Computed - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.Null (view.Width); Assert.Null (view.Height); view.BeginInit (); view.EndInit (); - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.Null (view.Width); Assert.Null (view.Height); view.SetRelativeLayout (new Rect (5, 5, 10, 10)); - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.Null (view.Width); Assert.Null (view.Height); @@ -118,17 +118,17 @@ public class SetRelativeLayoutTests { }; // Default layout style is Computed - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.NotNull (view.Width); Assert.NotNull (view.Height); view.BeginInit (); view.EndInit (); - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.NotNull (view.Width); Assert.NotNull (view.Height); view.SetRelativeLayout (new Rect (5, 5, 10, 10)); - Assert.Equal (view.LayoutStyle, LayoutStyle.Computed); + Assert.Equal (LayoutStyle.Computed, view.LayoutStyle); Assert.NotNull (view.Width); Assert.NotNull (view.Height); @@ -139,6 +139,93 @@ public class SetRelativeLayoutTests { Assert.Equal (expectedDim, view.Frame.Height); } + [Fact] + public void Fill_Pos_Within_Bounds () + { + var screen = new Rect (0, 0, 80, 25); + var view = new View () { + X = 1, + Y = 1, + Width = 5, + Height = 4 + }; + + view.SetRelativeLayout (screen); + Assert.Equal (1, view.Frame.X); + Assert.Equal (1, view.Frame.Y); + Assert.Equal (5, view.Frame.Width); + Assert.Equal (4, view.Frame.Height); + + view.Width = 80; + view.Height = 25; + view.SetRelativeLayout (screen); + Assert.Equal (1, view.Frame.X); + Assert.Equal (1, view.Frame.Y); + Assert.Equal (80, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + view.Width = Dim.Fill (); + view.Height = Dim.Fill (); + view.SetRelativeLayout (screen); + Assert.Equal (1, view.Frame.X); + Assert.Equal (1, view.Frame.Y); + Assert.Equal (79, view.Frame.Width); // proof (80 - 1) + Assert.Equal (24, view.Frame.Height); // proof (25 - 1) + + view.X = 79; + view.Width = Dim.Fill (); + view.Height = Dim.Fill (); + view.SetRelativeLayout (screen); + Assert.Equal (79, view.Frame.X); + Assert.Equal (1, view.Frame.Y); + Assert.Equal (1, view.Frame.Width); // proof (80 - 79) + Assert.Equal (24, view.Frame.Height); + + view.X = 80; + view.Width = Dim.Fill (); + view.Height = Dim.Fill (); + view.SetRelativeLayout (screen); + Assert.Equal (80, view.Frame.X); + Assert.Equal (1, view.Frame.Y); + Assert.Equal (0, view.Frame.Width); // proof (80 - 80) + Assert.Equal (24, view.Frame.Height); + } + + [Fact] + public void FIll_Pos_Outside_Bounds () + { + var screen = new Rect (0, 0, 80, 25); + var view = new View () { + X = 90, // outside of screen +10 + Y = -10, // outside of screen -10 + Width = 15, + Height = 15 + }; + + view.SetRelativeLayout (screen); + Assert.Equal (90, view.Frame.X); + Assert.Equal (-10, view.Frame.Y); + Assert.Equal (15, view.Frame.Width); + Assert.Equal (15, view.Frame.Height); + + // prove Width=Height= same as screen size + view.Width = 80; + view.Height = 25; + view.SetRelativeLayout (screen); + Assert.Equal (90, view.Frame.X); + Assert.Equal (-10, view.Frame.Y); + Assert.Equal (80, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + view.Width = Dim.Fill (); + view.Height = Dim.Fill (); + view.SetRelativeLayout (screen); + Assert.Equal (90, view.Frame.X); + Assert.Equal (-10, view.Frame.Y); + Assert.Equal (0, view.Frame.Width); // proof: 15x15 view is placed beyond right side of screen, so fill width is 0 + Assert.Equal (35, view.Frame.Height); // proof: 15x15 view is placed beyond top of screen 10 rows, screen is 25 rows. so fill height is 25 + 10 = 35 + } + [Fact] public void PosCombine_PosCenter_Minus_Absolute () { @@ -148,31 +235,114 @@ public class SetRelativeLayoutTests { var screen = new Rect (0, 0, 80, 25); var view = new View () { - X = Pos.Center () - 41, // ((80 / 2) - (5 / 2)) - 41 = (40 - 2 - 41) = -3 - Y = Pos.Center () - 13, // ((25 / 2) - (4 / 2)) - 13 = (12 - 2 - 13) = -3 - Width = 5, - Height = 4 + X = Pos.Center () - 41, // -2 off left edge of screen + Y = Pos.Center () - 13, // -1 off top edge of screen + Width = 1, + Height = 1 }; view.SetRelativeLayout (screen); - Assert.Equal (-21, view.Frame.X); // BUGBUG: Should be -3 - Assert.Equal (-7, view.Frame.Y); // BUGBUG: Should be -3 + Assert.Equal (-2, view.Frame.X); // proof: 1x1 view centered in 80x25 screen has x of 39, so -41 is -2 + Assert.Equal (-1, view.Frame.Y); // proof: 1x1 view centered in 80x25 screen has y of 12, so -13 is -1 + + view.Width = 80; + view.Height = 25; + view.SetRelativeLayout (screen); + Assert.Equal (-41, view.Frame.X); + Assert.Equal (-13, view.Frame.Y); + Assert.Equal (80, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + view.Width = Dim.Fill (); + view.Height = Dim.Fill (); + view.SetRelativeLayout (screen); + Assert.Equal (-41, view.Frame.X); + Assert.Equal (-13, view.Frame.Y); + Assert.Equal (121, view.Frame.Width); // 121 = screen.Width - (-Center - 41) + Assert.Equal (38, view.Frame.Height); } + [Fact] + public void FIll_And_PosCenter () + { + var screen = new Rect (0, 0, 80, 25); + var view = new View () { + X = Pos.Center (), + Y = Pos.Center (), + Width = Dim.Fill(), + Height = Dim.Fill() + }; + + view.SetRelativeLayout (screen); + Assert.Equal (0, view.Frame.X); + Assert.Equal (0, view.Frame.Y); + Assert.Equal (80, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + view.X = Pos.Center () + 1; + view.SetRelativeLayout (screen); + Assert.Equal (1, view.Frame.X); + Assert.Equal (0, view.Frame.Y); + Assert.Equal (79, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + view.X = Pos.Center () + 79; + view.SetRelativeLayout (screen); + Assert.Equal (79, view.Frame.X); + Assert.Equal (0, view.Frame.Y); + Assert.Equal (1, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + view.X = Pos.Center () + 80; + view.SetRelativeLayout (screen); + Assert.Equal (80, view.Frame.X); + Assert.Equal (0, view.Frame.Y); + Assert.Equal (0, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + view.X = Pos.Center () - 1; + view.SetRelativeLayout (screen); + Assert.Equal (-1, view.Frame.X); + Assert.Equal (0, view.Frame.Y); + Assert.Equal (81, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + view.X = Pos.Center () - 2; // Fill means all the way to right. So width will be 82. (dim gets calc'd before pos). + view.SetRelativeLayout (screen); + Assert.Equal (-2, view.Frame.X); + Assert.Equal (0, view.Frame.Y); + Assert.Equal (82, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + view.X = Pos.Center () - 3; // Fill means all the way to right. So width will be 83. (dim gets calc'd before pos). + view.SetRelativeLayout (screen); + Assert.Equal (-3, view.Frame.X); + Assert.Equal (0, view.Frame.Y); + Assert.Equal (83, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + view.X = Pos.Center () - 41; // Fill means all the way to right. So width will be . (dim gets calc'd before pos). + view.SetRelativeLayout (screen); + Assert.Equal (-41, view.Frame.X); + Assert.Equal (0, view.Frame.Y); + Assert.Equal (121, view.Frame.Width); + Assert.Equal (25, view.Frame.Height); + + } [Fact] public void PosCombine_PosCenter_Plus_Absolute () { var screen = new Rect (0, 0, 80, 25); var view = new View () { - X = Pos.Center () + 41, // ((80 / 2) - (5 / 2)) + 41 = (40 - 2 + 41) = 79 + X = Pos.Center () + 41, // ((80 / 2) - (5 / 2)) + 41 = (40 - 3 + 41) = 78 Y = Pos.Center () + 13, // ((25 / 2) - (4 / 2)) + 13 = (12 - 2 + 13) = 23 Width = 5, Height = 4 }; view.SetRelativeLayout (screen); - Assert.Equal (79, view.Frame.X); // BUGBUG: Should be 79 - Assert.Equal (23, view.Frame.Y); // BUGBUG: Should be 23 + Assert.Equal (78, view.Frame.X); + Assert.Equal (23, view.Frame.Y); } [Fact] [TestRespondersDisposed] diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index 3d8e89c6f..c882daed2 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -547,13 +547,13 @@ namespace Terminal.Gui.ViewTests { // This test has been moved to SetRlativeLayoutTests because it is testing // SetRelativeLayout. In addition, the old test was bogus because it was testing the wrong thing (and // because in v1 Pos.Center was broken in this regard! - //view.X = Pos.Center () - 41; - //view.Y = Pos.Center () - 13; - //view.SetRelativeLayout (top.Bounds); - //top.LayoutSubviews (); // BUGBUG: v2 - ?? - //view.BoundsToScreen (0, 0, out rcol, out rrow); - //Assert.Equal (-41, rcol); - //Assert.Equal (-13, rrow); + view.X = Pos.Center () - 41; + view.Y = Pos.Center () - 13; + view.SetRelativeLayout (top.Bounds); + top.LayoutSubviews (); // BUGBUG: v2 - ?? + view.BoundsToScreen (0, 0, out rcol, out rrow); + Assert.Equal (-41, rcol); + Assert.Equal (-13, rrow); Application.End (runState); }