diff --git a/Terminal.Gui/View/View.cs b/Terminal.Gui/View/View.cs index f7be5dba3..85e69b59f 100644 --- a/Terminal.Gui/View/View.cs +++ b/Terminal.Gui/View/View.cs @@ -1395,7 +1395,8 @@ namespace Terminal.Gui { public Point ScreenToBounds (int x, int y) { if (SuperView == null) { - return new Point (x - Frame.X + GetBoundsOffset ().X, y - Frame.Y + GetBoundsOffset ().Y); + var boundsOffset = GetBoundsOffset (); + return new Point (x - Frame.X + boundsOffset.X, y - Frame.Y + boundsOffset.Y); } else { var parent = SuperView.ScreenToView (x, y); return new Point (parent.X - frame.X, parent.Y - frame.Y); @@ -1414,13 +1415,15 @@ namespace Terminal.Gui { /// , respectively. public virtual void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clamped = true) { - rcol = col + Frame.X + GetBoundsOffset ().X; - rrow = row + Frame.Y + GetBoundsOffset ().Y; + var boundsOffset = GetBoundsOffset (); + rcol = col + Frame.X + boundsOffset.X; + rrow = row + Frame.Y + boundsOffset.Y; var super = SuperView; while (super != null) { - rcol += super.Frame.X + super.GetBoundsOffset ().X; - rrow += super.Frame.Y + super.GetBoundsOffset ().Y; + boundsOffset = super.GetBoundsOffset (); + rcol += super.Frame.X + boundsOffset.X; + rrow += super.Frame.Y + boundsOffset.Y; super = super.SuperView; } @@ -2577,10 +2580,8 @@ namespace Terminal.Gui { } return; case Pos.PosCombine pc: - foreach (var v in from.InternalSubviews) { - 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; } } @@ -2601,10 +2602,8 @@ namespace Terminal.Gui { } return; case Dim.DimCombine dc: - foreach (var v in from.InternalSubviews) { - CollectDim (dc.left, from, ref nNodes, ref nEdges); - CollectDim (dc.right, from, ref nNodes, ref nEdges); - } + CollectDim (dc.left, from, ref nNodes, ref nEdges); + CollectDim (dc.right, from, ref nNodes, ref nEdges); break; } } @@ -2658,15 +2657,11 @@ namespace Terminal.Gui { if (edges.Any ()) { (var from, var to) = edges.First (); - if (from != superView?.GetTopSuperView (to, from)) { - if (!ReferenceEquals (from, to)) { - if (ReferenceEquals (from.SuperView, to)) { - throw new InvalidOperationException ($"ComputedLayout for \"{superView}\": \"{to}\" references a SubView (\"{from}\")."); - } else { - throw new InvalidOperationException ($"ComputedLayout for \"{superView}\": \"{from}\" linked with \"{to}\" was not found. Did you forget to add it to {superView}?"); - } + if (from != superView?.GetTopSuperView (to, from) && !ReferenceEquals (from, to)) { + if (ReferenceEquals (from.SuperView, to)) { + throw new InvalidOperationException ($"ComputedLayout for \"{superView}\": \"{to}\" references a SubView (\"{from}\")."); } else { - throw new InvalidOperationException ($"ComputedLayout for \"{superView}\": A recursive cycle was found in the relative Pos/Dim of the SubViews."); + throw new InvalidOperationException ($"ComputedLayout for \"{superView}\": \"{from}\" linked with \"{to}\" was not found. Did you forget to add it to {superView}?"); } } } @@ -3508,8 +3503,9 @@ namespace Terminal.Gui { if (start.InternalSubviews != null) { int count = start.InternalSubviews.Count; if (count > 0) { - var rx = x - (startFrame.X + start.GetBoundsOffset ().X); - var ry = y - (startFrame.Y + start.GetBoundsOffset ().Y); + var boundsOffset = start.GetBoundsOffset (); + var rx = x - (startFrame.X + boundsOffset.X); + var ry = y - (startFrame.Y + boundsOffset.Y); for (int i = count - 1; i >= 0; i--) { View v = start.InternalSubviews [i]; if (v.Visible && v.Frame.Contains (rx, ry)) { diff --git a/UnitTests/View/Layout/DimTests.cs b/UnitTests/View/Layout/DimTests.cs index 39ecf8b5f..07a8fb067 100644 --- a/UnitTests/View/Layout/DimTests.cs +++ b/UnitTests/View/Layout/DimTests.cs @@ -1266,5 +1266,49 @@ namespace Terminal.Gui.ViewTests { Assert.Equal (51, label.Frame.Height); } } + + [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 allowed + Height = Dim.Height (super), // this is allowed + }; + + super.Add (view); + super.BeginInit (); + super.EndInit (); + + var exception = Record.Exception (super.LayoutSubviews); + Assert.Null (exception); + } + + [Fact] + public void Dim_SyperView_Referencing_SubView_Does_Not_Throws () + { + var super = new View ("super") { + Width = 10, + Height = 10 + }; + var view2 = new View ("view2") { + Width = 10, + Height = 10, + }; + var view = new View ("view") { + Width = Dim.Width (view2), // this is not allowed + Height = Dim.Height (view2), // this is not allowed + }; + + view.Add (view2); + super.Add (view); + super.BeginInit (); + super.EndInit (); + + Assert.Throws (super.LayoutSubviews); + } } } diff --git a/UnitTests/View/Layout/LayoutTests.cs b/UnitTests/View/Layout/LayoutTests.cs index 53554d85f..483d5940b 100644 --- a/UnitTests/View/Layout/LayoutTests.cs +++ b/UnitTests/View/Layout/LayoutTests.cs @@ -43,7 +43,9 @@ namespace Terminal.Gui.ViewTests { var sub2 = new View (); root.Add (sub2); sub2.Width = Dim.Width (sub2); - Assert.Throws (() => root.LayoutSubviews ()); + + var exception = Record.Exception (root.LayoutSubviews); + Assert.Null (exception); } [Fact] @@ -1693,7 +1695,7 @@ Y switch (width) { case 1: - //Assert.Equal (new Rect (0, 0, 17, 0), subview.Frame); + Assert.Equal (new Rect (0, 0, 0, 4), subview.Frame); expected = @" │ │ @@ -1704,7 +1706,7 @@ Y │"; break; case 2: - //Assert.Equal (new Rect (0, 0, 17, 1), subview.Frame); + Assert.Equal (new Rect (0, 0, 0, 4), subview.Frame); expected = @" ┌┐ ││ @@ -1715,7 +1717,7 @@ Y └┘"; break; case 3: - //Assert.Equal (new Rect (0, 0, 17, 2), subview.Frame); + Assert.Equal (new Rect (0, 0, 0, 4), subview.Frame); expected = @" ┌─┐ │ │ @@ -1727,7 +1729,7 @@ Y "; break; case 4: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + Assert.Equal (new Rect (0, 0, 1, 4), subview.Frame); expected = @" ┌──┐ ││ │ @@ -1738,7 +1740,7 @@ Y └──┘"; break; case 5: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + Assert.Equal (new Rect (0, 0, 2, 4), subview.Frame); expected = @" ┌───┐ │┌┐ │ @@ -1749,7 +1751,7 @@ Y └───┘"; break; case 6: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + Assert.Equal (new Rect (0, 0, 3, 4), subview.Frame); expected = @" ┌────┐ │┌─┐ │ @@ -1760,7 +1762,7 @@ Y └────┘"; break; case 7: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + Assert.Equal (new Rect (0, 0, 4, 4), subview.Frame); expected = @" ┌─────┐ │┌──┐ │ @@ -1771,7 +1773,7 @@ Y └─────┘"; break; case 8: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + Assert.Equal (new Rect (0, 0, 5, 4), subview.Frame); expected = @" ┌──────┐ │┌───┐ │ @@ -1782,7 +1784,7 @@ Y └──────┘"; break; case 9: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + Assert.Equal (new Rect (1, 0, 5, 4), subview.Frame); expected = @" ┌───────┐ │ ┌───┐ │ @@ -1793,7 +1795,7 @@ Y └───────┘"; break; case 10: - //Assert.Equal (new Rect (0, 0, 17, 3), subview.Frame); + Assert.Equal (new Rect (1, 0, 6, 4), subview.Frame); expected = @" ┌────────┐ │ ┌────┐ │ @@ -1805,7 +1807,57 @@ Y break; } _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); + } + [Fact, AutoInitShutdown] + public void PosConbine_DimCombine_View_With_SubViews () + { + var clicked = false; + var top = Application.Top; + var win1 = new Window () { Id = "win1", Width = 20, Height = 10 }; + var btn = new Button ("ok"); + var win2 = new Window () { Id = "win2", Y = Pos.Bottom (btn) + 1, Width = 10, Height = 3 }; + var view1 = new View () { Id = "view1", Width = Dim.Fill (), Height = 1, CanFocus = true }; + view1.MouseClick += (sender, e) => clicked = true; + var view2 = new View () { Id = "view2", Width = Dim.Fill (1), Height = 1, CanFocus = true }; + + view1.Add (view2); + win2.Add (view1); + win1.Add (btn, win2); + top.Add (win1); + + var rs = Application.Begin (top); + + TestHelpers.AssertDriverContentsWithFrameAre (@" +┌──────────────────┐ +│[ ok ] │ +│ │ +│┌────────┐ │ +││ │ │ +│└────────┘ │ +│ │ +│ │ +│ │ +└──────────────────┘", output); + Assert.Equal (new Rect (0, 0, 80, 25), top.Frame); + Assert.Equal (new Rect (0, 0, 6, 1), btn.Frame); + Assert.Equal (new Rect (0, 0, 20, 10), win1.Frame); + Assert.Equal (new Rect (0, 2, 10, 3), win2.Frame); + Assert.Equal (new Rect (0, 0, 8, 1), view1.Frame); + Assert.Equal (new Rect (0, 0, 7, 1), view2.Frame); + var foundView = View.FindDeepestView (top, 9, 4, out int rx, out int ry); + Assert.Equal (foundView, view1); + ReflectionTools.InvokePrivate ( + typeof (Application), + "ProcessMouseEvent", + new MouseEvent () { + X = 9, + Y = 4, + Flags = MouseFlags.Button1Clicked + }); + Assert.True (clicked); + + Application.End (rs); } } }