Fixes #2503. PosCombine and DimCombine is wrongly looking into the from.InternalSubviews.

This commit is contained in:
BDisp
2023-04-05 23:58:15 +01:00
committed by Tig
parent c1a578891b
commit 16a6e581d1
3 changed files with 126 additions and 34 deletions

View File

@@ -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 {
/// <see cref="ConsoleDriver.Rows"/>, respectively.</param>
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)) {

View File

@@ -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<InvalidOperationException> (super.LayoutSubviews);
}
}
}

View File

@@ -43,7 +43,9 @@ namespace Terminal.Gui.ViewTests {
var sub2 = new View ();
root.Add (sub2);
sub2.Width = Dim.Width (sub2);
Assert.Throws<InvalidOperationException> (() => 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);
}
}
}