mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Merge pull request #3277 from tig/v2_3276_adornment_bounds
Fixes #3276. `Adornment.Bounds/FrameToScreen`
This commit is contained in:
@@ -1374,7 +1374,8 @@ public static partial class Application
|
||||
return;
|
||||
}
|
||||
|
||||
var view = View.FindDeepestView (Current, a.MouseEvent.X, a.MouseEvent.Y, out int screenX, out int screenY);
|
||||
// TODO: In PR #3273, FindDeepestView will return adornments. Update logic below to fix adornment mouse handling
|
||||
var view = View.FindDeepestView (Current, a.MouseEvent.X, a.MouseEvent.Y);
|
||||
|
||||
if (view is { WantContinuousButtonPressed: true })
|
||||
{
|
||||
@@ -1437,7 +1438,7 @@ public static partial class Application
|
||||
&& a.MouseEvent.Flags != 0)
|
||||
{
|
||||
View? top = FindDeepestTop (Top, a.MouseEvent.X, a.MouseEvent.Y);
|
||||
view = View.FindDeepestView (top, a.MouseEvent.X, a.MouseEvent.Y, out screenX, out screenY);
|
||||
view = View.FindDeepestView (top, a.MouseEvent.X, a.MouseEvent.Y);
|
||||
|
||||
if (view is { } && view != OverlappedTop && top != Current)
|
||||
{
|
||||
@@ -1450,6 +1451,8 @@ public static partial class Application
|
||||
return;
|
||||
}
|
||||
|
||||
var screen = view.FrameToScreen ();
|
||||
|
||||
// Work inside-out (Padding, Border, Margin)
|
||||
// TODO: Debate whether inside-out or outside-in is the right strategy
|
||||
if (AdornmentHandledMouseEvent (view.Padding, a))
|
||||
@@ -1469,11 +1472,11 @@ public static partial class Application
|
||||
|
||||
var me = new MouseEvent
|
||||
{
|
||||
X = screenX,
|
||||
Y = screenY,
|
||||
X = a.MouseEvent.X - screen.X,
|
||||
Y = a.MouseEvent.Y - screen.Y,
|
||||
Flags = a.MouseEvent.Flags,
|
||||
OfX = screenX,
|
||||
OfY = screenY,
|
||||
OfX = a.MouseEvent.X - screen.X,
|
||||
OfY = a.MouseEvent.Y - screen.Y,
|
||||
View = view
|
||||
};
|
||||
|
||||
|
||||
@@ -57,6 +57,9 @@ public class Responder : IDisposable
|
||||
public event EventHandler Disposing;
|
||||
|
||||
/// <summary>Method invoked when a mouse event is generated</summary>
|
||||
/// <remarks>
|
||||
/// The coordinates are relative to <see cref="View.Bounds"/>.
|
||||
/// </remarks>
|
||||
/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
|
||||
/// <param name="mouseEvent">Contains the details about the mouse event.</param>
|
||||
public virtual bool MouseEvent (MouseEvent mouseEvent) { return false; }
|
||||
@@ -81,6 +84,9 @@ public class Responder : IDisposable
|
||||
/// Called when the mouse first enters the view; the view will now receives mouse events until the mouse leaves
|
||||
/// the view. At which time, <see cref="OnMouseLeave(Gui.MouseEvent)"/> will be called.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The coordinates are relative to <see cref="View.Bounds"/>.
|
||||
/// </remarks>
|
||||
/// <param name="mouseEvent"></param>
|
||||
/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
|
||||
public virtual bool OnMouseEnter (MouseEvent mouseEvent) { return false; }
|
||||
@@ -89,6 +95,9 @@ public class Responder : IDisposable
|
||||
/// Called when the mouse has moved outside of the view; the view will no longer receive mouse events (until the
|
||||
/// mouse moves within the view again and <see cref="OnMouseEnter(Gui.MouseEvent)"/> is called).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The coordinates are relative to <see cref="View.Bounds"/>.
|
||||
/// </remarks>
|
||||
/// <param name="mouseEvent"></param>
|
||||
/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
|
||||
public virtual bool OnMouseLeave (MouseEvent mouseEvent) { return false; }
|
||||
|
||||
@@ -33,7 +33,7 @@ public class Adornment : View
|
||||
/// <summary>Gets the rectangle that describes the inner area of the Adornment. The Location is always (0,0).</summary>
|
||||
public override Rectangle Bounds
|
||||
{
|
||||
get => Thickness?.GetInside (new (Point.Empty, Frame.Size)) ?? new Rectangle (Point.Empty, Frame.Size);
|
||||
get => new Rectangle (Point.Empty, Thickness?.GetInside (new (Point.Empty, Frame.Size)).Size ?? Frame.Size);
|
||||
// QUESTION: So why even have a setter then?
|
||||
set => throw new InvalidOperationException ("It makes no sense to set Bounds of a Thickness.");
|
||||
}
|
||||
@@ -100,17 +100,19 @@ public class Adornment : View
|
||||
/// <inheritdoc/>
|
||||
public override Rectangle FrameToScreen ()
|
||||
{
|
||||
if (Parent is null)
|
||||
{
|
||||
return Frame;
|
||||
}
|
||||
|
||||
// Adornments are *Children* of a View, not SubViews. Thus View.FrameToScreen will not work.
|
||||
// To get the screen-relative coordinates of a Adornment, we need to know who
|
||||
// the Parent is
|
||||
Rectangle ret = Parent?.Frame ?? Frame;
|
||||
ret.Size = Frame.Size;
|
||||
|
||||
ret.Location = Parent?.FrameToScreen ().Location ?? ret.Location;
|
||||
Rectangle parent = Parent.FrameToScreen ();
|
||||
|
||||
// We now have coordinates relative to our View. If our View's SuperView has
|
||||
// a SuperView, keep going...
|
||||
return ret;
|
||||
return new (new (parent.X + Frame.X, parent.Y + Frame.Y), Frame.Size);
|
||||
}
|
||||
|
||||
/// <summary>Does nothing for Adornment</summary>
|
||||
|
||||
@@ -545,28 +545,49 @@ public partial class View
|
||||
}
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
/// <summary>Finds which view that belong to the <paramref name="start"/> superview at the provided location.</summary>
|
||||
/// <param name="start">The superview where to look for.</param>
|
||||
/// <param name="x">The column location in the superview.</param>
|
||||
/// <param name="y">The row location in the superview.</param>
|
||||
/// <param name="resultX">The found view screen relative column location.</param>
|
||||
/// <param name="resultY">The found view screen relative row location.</param>
|
||||
/// <param name="findAdornments">TODO: Remove this in PR #3273</param>
|
||||
/// <returns>
|
||||
/// The view that was found at the <paramref name="x"/> and <paramref name="y"/> coordinates.
|
||||
/// <see langword="null"/> if no view was found.
|
||||
/// </returns>
|
||||
|
||||
// CONCURRENCY: This method is not thread-safe.
|
||||
// Undefined behavior and likely program crashes are exposed by unsynchronized access to InternalSubviews.
|
||||
public static View? FindDeepestView (View? start, int x, int y, out int resultX, out int resultY)
|
||||
public static View? FindDeepestView (View? start, int x, int y, bool findAdornments = false)
|
||||
{
|
||||
resultY = resultX = 0;
|
||||
|
||||
if (start is null || !start.Frame.Contains (x, y))
|
||||
if (start is null || !start.Visible)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!start.Frame.Contains (x, y))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (findAdornments)
|
||||
{
|
||||
// TODO: This is a temporary hack for PR #3273; it is not actually used anywhere but unit tests at this point.
|
||||
if (start.Margin.Thickness.Contains (start.Margin.Frame, x, y))
|
||||
{
|
||||
return start.Margin;
|
||||
}
|
||||
if (start.Border.Thickness.Contains (start.Border.Frame, x, y))
|
||||
{
|
||||
return start.Border;
|
||||
}
|
||||
if (start.Padding.Thickness.Contains (start.Padding.Frame, x, y))
|
||||
{
|
||||
return start.Padding;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (start.InternalSubviews is { Count: > 0 })
|
||||
{
|
||||
Point boundsOffset = start.GetBoundsOffset ();
|
||||
@@ -579,19 +600,14 @@ public partial class View
|
||||
|
||||
if (v.Visible && v.Frame.Contains (rx, ry))
|
||||
{
|
||||
View? deep = FindDeepestView (v, rx, ry, out resultX, out resultY);
|
||||
|
||||
View? deep = FindDeepestView (v, rx, ry, findAdornments);
|
||||
return deep ?? v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultX = x - start.Frame.X;
|
||||
resultY = y - start.Frame.Y;
|
||||
|
||||
return start;
|
||||
}
|
||||
#nullable restore
|
||||
#nullable restore
|
||||
|
||||
/// <summary>Gets the <see cref="Frame"/> with a screen-relative location.</summary>
|
||||
/// <returns>The location and size of the view in screen-relative coordinates.</returns>
|
||||
|
||||
@@ -727,13 +727,7 @@ internal sealed class Menu : View
|
||||
locationOffset.Y += SuperView.Border.Thickness.Top;
|
||||
}
|
||||
|
||||
View view = FindDeepestView (
|
||||
this,
|
||||
a.MouseEvent.X + locationOffset.X,
|
||||
a.MouseEvent.Y + locationOffset.Y,
|
||||
out int rx,
|
||||
out int ry
|
||||
);
|
||||
View view = FindDeepestView (this, a.MouseEvent.X + locationOffset.X, a.MouseEvent.Y + locationOffset.Y);
|
||||
|
||||
if (view == this)
|
||||
{
|
||||
@@ -742,7 +736,13 @@ internal sealed class Menu : View
|
||||
throw new InvalidOperationException ("This shouldn't running on a invisible menu!");
|
||||
}
|
||||
|
||||
var nme = new MouseEvent { X = rx, Y = ry, Flags = a.MouseEvent.Flags, View = view };
|
||||
var screen = view.FrameToScreen ();
|
||||
var nme = new MouseEvent {
|
||||
X = a.MouseEvent.X - screen.X,
|
||||
Y = a.MouseEvent.Y - screen.Y,
|
||||
Flags = a.MouseEvent.Flags,
|
||||
View = view
|
||||
};
|
||||
|
||||
if (MouseEvent (nme) || a.MouseEvent.Flags == MouseFlags.Button1Pressed || a.MouseEvent.Flags == MouseFlags.Button1Released)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,46 @@ public class AdornmentTests
|
||||
private readonly ITestOutputHelper _output;
|
||||
public AdornmentTests (ITestOutputHelper output) { _output = output; }
|
||||
|
||||
[Fact]
|
||||
public void Bounds_Location_Always_Empty_Size_Correct ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
X = 1,
|
||||
Y = 2,
|
||||
Width = 20,
|
||||
Height = 31
|
||||
};
|
||||
|
||||
var marginThickness = 1;
|
||||
view.Margin.Thickness = new Thickness (marginThickness);
|
||||
|
||||
var borderThickness = 2;
|
||||
view.Border.Thickness = new Thickness (borderThickness);
|
||||
|
||||
var paddingThickness = 3;
|
||||
view.Padding.Thickness = new Thickness (paddingThickness);
|
||||
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
|
||||
Assert.Equal (new Rectangle (1, 2, 20, 31), view.Frame);
|
||||
Assert.Equal (new Rectangle (0, 0, 8, 19), view.Bounds);
|
||||
|
||||
Assert.Equal (new Rectangle (0, 0, view.Margin.Frame.Width - marginThickness * 2, view.Margin.Frame.Height - marginThickness * 2), view.Margin.Bounds);
|
||||
|
||||
Assert.Equal (new Rectangle (0, 0, view.Border.Frame.Width - borderThickness * 2, view.Border.Frame.Height - borderThickness * 2), view.Border.Bounds);
|
||||
|
||||
Assert.Equal (
|
||||
new Rectangle (
|
||||
0,
|
||||
0,
|
||||
view.Padding.Frame.Width - (marginThickness + borderThickness) * 2,
|
||||
view.Padding.Frame.Height - (marginThickness + borderThickness) * 2),
|
||||
view.Padding.Bounds);
|
||||
}
|
||||
|
||||
// Test that Adornment.Bounds_get override uses Parent not SuperView
|
||||
[Fact]
|
||||
public void BoundsToScreen_Uses_Parent_Not_SuperView ()
|
||||
{
|
||||
@@ -25,6 +65,134 @@ public class AdornmentTests
|
||||
Assert.Equal (new Rectangle (2, 4, 5, 5), boundsAsScreen);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Frames_are_Parent_SuperView_Relative ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
X = 1,
|
||||
Y = 2,
|
||||
Width = 20,
|
||||
Height = 31
|
||||
};
|
||||
|
||||
var marginThickness = 1;
|
||||
view.Margin.Thickness = new Thickness (marginThickness);
|
||||
|
||||
var borderThickness = 2;
|
||||
view.Border.Thickness = new Thickness (borderThickness);
|
||||
|
||||
var paddingThickness = 3;
|
||||
view.Padding.Thickness = new Thickness (paddingThickness);
|
||||
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
|
||||
Assert.Equal (new Rectangle (1, 2, 20, 31), view.Frame);
|
||||
Assert.Equal (new Rectangle (0, 0, 8, 19), view.Bounds);
|
||||
|
||||
// Margin.Frame is always the same as the view frame
|
||||
Assert.Equal (new Rectangle (0, 0, 20, 31), view.Margin.Frame);
|
||||
|
||||
// Border.Frame is View.Frame minus the Margin thickness
|
||||
Assert.Equal (
|
||||
new Rectangle (marginThickness, marginThickness, view.Frame.Width - marginThickness * 2, view.Frame.Height - marginThickness * 2),
|
||||
view.Border.Frame);
|
||||
|
||||
// Padding.Frame is View.Frame minus the Border thickness plus Margin thickness
|
||||
Assert.Equal (
|
||||
new Rectangle (
|
||||
marginThickness + borderThickness,
|
||||
marginThickness + borderThickness,
|
||||
view.Frame.Width - (marginThickness + borderThickness) * 2,
|
||||
view.Frame.Height - (marginThickness + borderThickness) * 2),
|
||||
view.Padding.Frame);
|
||||
}
|
||||
|
||||
// Test that Adornment.FrameToScreen override retains Frame.Size
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0)]
|
||||
[InlineData (0, 1, 1)]
|
||||
[InlineData (0, 10, 10)]
|
||||
[InlineData (1, 0, 0)]
|
||||
[InlineData (1, 1, 1)]
|
||||
[InlineData (1, 10, 10)]
|
||||
public void FrameToScreen_Retains_Frame_Size (int marginThickness, int w, int h)
|
||||
{
|
||||
var parent = new View { X = 1, Y = 2, Width = w, Height = h };
|
||||
parent.Margin.Thickness = new Thickness (marginThickness);
|
||||
|
||||
parent.BeginInit ();
|
||||
parent.EndInit ();
|
||||
|
||||
Assert.Equal (new Rectangle (1, 2, w, h), parent.Frame);
|
||||
Assert.Equal (new Rectangle (0, 0, w, h), parent.Margin.Frame);
|
||||
|
||||
Assert.Equal (parent.Frame, parent.Margin.FrameToScreen ());
|
||||
}
|
||||
|
||||
// Test that Adornment.FrameToScreen override returns Frame if Parent is null
|
||||
[Fact]
|
||||
public void FrameToScreen_Returns_Frame_If_Parent_Is_Null ()
|
||||
{
|
||||
var a = new Adornment
|
||||
{
|
||||
X = 1,
|
||||
Y = 2,
|
||||
Width = 3,
|
||||
Height = 4
|
||||
};
|
||||
|
||||
Assert.Null (a.Parent);
|
||||
Assert.Equal (a.Frame, a.FrameToScreen ());
|
||||
}
|
||||
|
||||
// Test that Adornment.FrameToScreen override returns correct location
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0, 0)]
|
||||
[InlineData (0, 0, 1, 1)]
|
||||
[InlineData (0, 0, 10, 10)]
|
||||
[InlineData (1, 0, 0, 0)]
|
||||
[InlineData (1, 0, 1, 1)]
|
||||
[InlineData (1, 0, 10, 10)]
|
||||
[InlineData (0, 1, 0, 0)]
|
||||
[InlineData (0, 1, 1, 1)]
|
||||
[InlineData (0, 1, 10, 10)]
|
||||
[InlineData (1, 1, 0, 0)]
|
||||
[InlineData (1, 1, 1, 1)]
|
||||
[InlineData (1, 1, 10, 10)]
|
||||
public void FrameToScreen_Returns_Screen_Location (int marginThickness, int borderThickness, int x, int y)
|
||||
{
|
||||
var superView = new View
|
||||
{
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = 20,
|
||||
Height = 20
|
||||
};
|
||||
superView.Margin.Thickness = new Thickness (marginThickness);
|
||||
superView.Border.Thickness = new Thickness (borderThickness);
|
||||
|
||||
var view = new View { X = x, Y = y, Width = 1, Height = 1 };
|
||||
superView.Add (view);
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
|
||||
Assert.Equal (new Rectangle (x, y, 1, 1), view.Frame);
|
||||
Assert.Equal (new Rectangle (0, 0, 20, 20), superView.Margin.Frame);
|
||||
|
||||
Assert.Equal (
|
||||
new Rectangle (marginThickness, marginThickness, 20 - marginThickness * 2, 20 - marginThickness * 2),
|
||||
superView.Border.Frame
|
||||
);
|
||||
|
||||
Assert.Equal (
|
||||
new Rectangle (superView.Frame.X + marginThickness, superView.Frame.Y + marginThickness, 20 - marginThickness * 2, 20 - marginThickness * 2),
|
||||
superView.Border.FrameToScreen ()
|
||||
);
|
||||
}
|
||||
|
||||
// Test that Adornment.FrameToScreen override uses Parent not SuperView
|
||||
[Fact]
|
||||
public void FrameToScreen_Uses_Parent_Not_SuperView ()
|
||||
{
|
||||
|
||||
285
UnitTests/View/FindDeepestViewTests.cs
Normal file
285
UnitTests/View/FindDeepestViewTests.cs
Normal file
@@ -0,0 +1,285 @@
|
||||
using UICatalog.Scenarios;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests View.FindDeepestView
|
||||
/// </summary>
|
||||
/// <param name="output"></param>
|
||||
public class FindDeepestViewTests (ITestOutputHelper output)
|
||||
{
|
||||
// Test that FindDeepestView returns the correct view if the start view has no subviews
|
||||
[Theory]
|
||||
[InlineData (0, 0)]
|
||||
[InlineData (1, 1)]
|
||||
[InlineData (2, 2)]
|
||||
public void Returns_Start_If_No_SubViews (int testX, int testY)
|
||||
{
|
||||
var start = new View ()
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
};
|
||||
|
||||
Assert.Same (start, View.FindDeepestView (start, testX, testY));
|
||||
}
|
||||
|
||||
// Test that FindDeepestView returns null if the start view has no subviews and coords are outside the view
|
||||
[Theory]
|
||||
[InlineData (0, 0)]
|
||||
[InlineData (2, 1)]
|
||||
[InlineData (20, 20)]
|
||||
public void Returns_Null_If_No_SubViews_Coords_Outside (int testX, int testY)
|
||||
{
|
||||
var start = new View ()
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 10, Height = 10,
|
||||
};
|
||||
|
||||
Assert.Null(View.FindDeepestView (start, testX, testY));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0)]
|
||||
[InlineData (2, 1)]
|
||||
[InlineData (20, 20)]
|
||||
public void Returns_Null_If_Start_Not_Visible (int testX, int testY)
|
||||
{
|
||||
var start = new View ()
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 10, Height = 10,
|
||||
Visible = false,
|
||||
};
|
||||
|
||||
Assert.Null (View.FindDeepestView (start, testX, testY));
|
||||
}
|
||||
|
||||
// Test that FindDeepestView returns the correct view if the start view has subviews
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
|
||||
[InlineData (1, 2, true)]
|
||||
[InlineData (5, 6, true)]
|
||||
public void Returns_Correct_If_SubViews (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
var start = new View ()
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
};
|
||||
|
||||
var subview = new View ()
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 5, Height = 5,
|
||||
};
|
||||
start.Add (subview);
|
||||
|
||||
var found = View.FindDeepestView (start, testX, testY);
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
[InlineData (1, 2, false)]
|
||||
[InlineData (5, 6, false)]
|
||||
public void Returns_Null_If_SubView_NotVisible (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
var start = new View ()
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
};
|
||||
|
||||
var subview = new View ()
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 5, Height = 5,
|
||||
Visible = false
|
||||
};
|
||||
start.Add (subview);
|
||||
|
||||
var found = View.FindDeepestView (start, testX, testY);
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
[InlineData (1, 2, false)]
|
||||
[InlineData (5, 6, false)]
|
||||
public void Returns_Null_If_Not_Visible_And_SubView_Visible (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
var start = new View ()
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
var subview = new View ()
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 5, Height = 5,
|
||||
};
|
||||
start.Add (subview);
|
||||
subview.Visible = true;
|
||||
Assert.True (subview.Visible);
|
||||
Assert.False (start.Visible);
|
||||
var found = View.FindDeepestView (start, testX, testY);
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
}
|
||||
|
||||
// Test that FindDeepestView works if the start view has positive Adornments
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (7, 8, false)]
|
||||
[InlineData (1, 2, false)]
|
||||
|
||||
[InlineData (2, 3, true)]
|
||||
[InlineData (5, 6, true)]
|
||||
[InlineData (2, 3, true)]
|
||||
[InlineData (6, 7, true)]
|
||||
public void Returns_Correct_If_Start_Has_Adornments (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
var start = new View ()
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
};
|
||||
start.Margin.Thickness = new Thickness (1);
|
||||
|
||||
var subview = new View ()
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 5, Height = 5,
|
||||
};
|
||||
start.Add (subview);
|
||||
|
||||
var found = View.FindDeepestView (start, testX, testY, true);
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, typeof(Margin))]
|
||||
[InlineData (9, 9, typeof (Margin))]
|
||||
|
||||
[InlineData (1, 1, typeof (Border))]
|
||||
[InlineData (8, 8, typeof (Border))]
|
||||
|
||||
[InlineData (2, 2, typeof (Padding))]
|
||||
[InlineData (7, 7, typeof (Padding))]
|
||||
|
||||
[InlineData (5, 5, typeof (View))]
|
||||
public void Returns_Adornment_If_Start_Has_Adornments (int testX, int testY, Type expectedAdornmentType)
|
||||
{
|
||||
var start = new View ()
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
};
|
||||
start.Margin.Thickness = new Thickness (1);
|
||||
start.Border.Thickness = new Thickness (1);
|
||||
start.Padding.Thickness = new Thickness (1);
|
||||
|
||||
var subview = new View ()
|
||||
{
|
||||
X = 1, Y = 1,
|
||||
Width = 1, Height = 1,
|
||||
};
|
||||
start.Add (subview);
|
||||
|
||||
var found = View.FindDeepestView (start, testX, testY, true);
|
||||
Assert.Equal(expectedAdornmentType, found.GetType());
|
||||
}
|
||||
|
||||
// Test that FindDeepestView works if the subview has positive Adornments
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (7, 8, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
|
||||
[InlineData (1, 2, true)]
|
||||
[InlineData (2, 3, true)]
|
||||
[InlineData (5, 6, true)]
|
||||
[InlineData (2, 3, true)]
|
||||
public void Returns_Correct_If_SubView_Has_Adornments (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
var start = new View ()
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
};
|
||||
|
||||
var subview = new View ()
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 5, Height = 5,
|
||||
};
|
||||
subview.Margin.Thickness = new Thickness (1);
|
||||
start.Add (subview);
|
||||
|
||||
var found = View.FindDeepestView (start, testX, testY);
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
}
|
||||
|
||||
// Test that FindDeepestView works with nested subviews
|
||||
[Theory]
|
||||
[InlineData (0, 0, -1)]
|
||||
[InlineData (9, 9, -1)]
|
||||
[InlineData (10, 10, -1)]
|
||||
|
||||
[InlineData (1, 1, 0)]
|
||||
[InlineData (1, 2, 0)]
|
||||
[InlineData (2, 2, 1)]
|
||||
[InlineData (3, 3, 2)]
|
||||
[InlineData (5, 5, 2)]
|
||||
public void Returns_Correct_With_NestedSubViews (int testX, int testY, int expectedSubViewFound)
|
||||
{
|
||||
var start = new View ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
|
||||
int numSubViews = 3;
|
||||
List<View> subviews = new List<View> ();
|
||||
for (int i = 0; i < numSubViews; i++)
|
||||
{
|
||||
var subview = new View ()
|
||||
{
|
||||
X = 1, Y = 1,
|
||||
Width = 5, Height = 5,
|
||||
};
|
||||
subviews.Add (subview);
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
subviews [i - 1].Add (subview);
|
||||
}
|
||||
}
|
||||
|
||||
start.Add (subviews [0]);
|
||||
|
||||
var found = View.FindDeepestView (start, testX, testY);
|
||||
Assert.Equal (expectedSubViewFound, subviews.IndexOf(found));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Xunit.Abstractions;
|
||||
using static Unix.Terminal.Curses;
|
||||
|
||||
// Alias Console to MockConsole so we don't accidentally use Console
|
||||
|
||||
@@ -549,7 +550,7 @@ public class LayoutTests
|
||||
Assert.Equal (new Rectangle (0, 2, 10, 3), win2.Frame);
|
||||
Assert.Equal (new Rectangle (0, 0, 8, 1), view2.Frame);
|
||||
Assert.Equal (new Rectangle (0, 0, 7, 1), view3.Frame);
|
||||
var foundView = View.FindDeepestView (top, 9, 4, out int rx, out int ry);
|
||||
var foundView = View.FindDeepestView (top, 9, 4);
|
||||
Assert.Equal (foundView, view2);
|
||||
|
||||
Application.OnMouseEvent (
|
||||
|
||||
@@ -856,39 +856,46 @@ public class NavigationTests
|
||||
top.BoundsToScreen (-1, -1, out col, out row);
|
||||
Assert.Equal (0, col);
|
||||
Assert.Equal (0, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 0, 0, out int rx, out int ry));
|
||||
Assert.Equal (0, rx);
|
||||
Assert.Equal (0, ry);
|
||||
var found = View.FindDeepestView (top, 0, 0);
|
||||
Assert.Equal (top, found);
|
||||
|
||||
Assert.Equal (0, found.Frame.X);
|
||||
Assert.Equal (0, found.Frame.Y);
|
||||
Assert.Equal (new Point (3, 2), top.ScreenToFrame (3, 2));
|
||||
top.BoundsToScreen (3, 2, out col, out row);
|
||||
Assert.Equal (4, col);
|
||||
Assert.Equal (3, row);
|
||||
Assert.Equal (view, View.FindDeepestView (top, col, row, out rx, out ry));
|
||||
Assert.Equal (0, rx);
|
||||
Assert.Equal (0, ry);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 3, 2, out rx, out ry));
|
||||
Assert.Equal (3, rx);
|
||||
Assert.Equal (2, ry);
|
||||
found = View.FindDeepestView (top, col, row);
|
||||
Assert.Equal (view, found);
|
||||
//Assert.Equal (0, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
found = View.FindDeepestView (top, 3, 2);
|
||||
Assert.Equal (top, found);
|
||||
//Assert.Equal (3, found.FrameToScreen ().X);
|
||||
//Assert.Equal (2, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (13, 2), top.ScreenToFrame (13, 2));
|
||||
top.BoundsToScreen (12, 2, out col, out row);
|
||||
Assert.Equal (13, col);
|
||||
Assert.Equal (3, row);
|
||||
Assert.Equal (view, View.FindDeepestView (top, col, row, out rx, out ry));
|
||||
Assert.Equal (9, rx);
|
||||
Assert.Equal (0, ry);
|
||||
found = View.FindDeepestView (top, col, row);
|
||||
Assert.Equal (view, found);
|
||||
//Assert.Equal (9, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
top.BoundsToScreen (13, 2, out col, out row);
|
||||
Assert.Equal (14, col);
|
||||
Assert.Equal (3, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 13, 2, out rx, out ry));
|
||||
Assert.Equal (13, rx);
|
||||
Assert.Equal (2, ry);
|
||||
found = View.FindDeepestView (top, 13, 2);
|
||||
Assert.Equal (top, found);
|
||||
//Assert.Equal (13, found.FrameToScreen ().X);
|
||||
//Assert.Equal (2, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (14, 3), top.ScreenToFrame (14, 3));
|
||||
top.BoundsToScreen (14, 3, out col, out row);
|
||||
Assert.Equal (15, col);
|
||||
Assert.Equal (4, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 14, 3, out rx, out ry));
|
||||
Assert.Equal (14, rx);
|
||||
Assert.Equal (3, ry);
|
||||
found = View.FindDeepestView (top, 14, 3);
|
||||
Assert.Equal (top, found);
|
||||
//Assert.Equal (14, found.FrameToScreen ().X);
|
||||
//Assert.Equal (3, found.FrameToScreen ().Y);
|
||||
|
||||
// view
|
||||
Assert.Equal (new Point (-4, -3), view.ScreenToFrame (0, 0));
|
||||
@@ -907,30 +914,34 @@ public class NavigationTests
|
||||
view.BoundsToScreen (-4, -3, out col, out row);
|
||||
Assert.Equal (0, col);
|
||||
Assert.Equal (0, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 0, 0, out rx, out ry));
|
||||
Assert.Equal (0, rx);
|
||||
Assert.Equal (0, ry);
|
||||
found = View.FindDeepestView (top, 0, 0);
|
||||
Assert.Equal (top, found);
|
||||
//Assert.Equal (0, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (-1, -1), view.ScreenToFrame (3, 2));
|
||||
view.BoundsToScreen (0, 0, out col, out row);
|
||||
Assert.Equal (4, col);
|
||||
Assert.Equal (3, row);
|
||||
Assert.Equal (view, View.FindDeepestView (top, 4, 3, out rx, out ry));
|
||||
Assert.Equal (0, rx);
|
||||
Assert.Equal (0, ry);
|
||||
found = View.FindDeepestView (top, 4, 3);
|
||||
Assert.Equal (view, found);
|
||||
//Assert.Equal (0, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (9, -1), view.ScreenToFrame (13, 2));
|
||||
view.BoundsToScreen (10, 0, out col, out row);
|
||||
Assert.Equal (14, col);
|
||||
Assert.Equal (3, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 14, 3, out rx, out ry));
|
||||
Assert.Equal (14, rx);
|
||||
Assert.Equal (3, ry);
|
||||
found = View.FindDeepestView (top, 14, 3);
|
||||
Assert.Equal (top, found);
|
||||
//Assert.Equal (14, found.FrameToScreen ().X);
|
||||
//Assert.Equal (3, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (10, 0), view.ScreenToFrame (14, 3));
|
||||
view.BoundsToScreen (11, 1, out col, out row);
|
||||
Assert.Equal (15, col);
|
||||
Assert.Equal (4, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 15, 4, out rx, out ry));
|
||||
Assert.Equal (15, rx);
|
||||
Assert.Equal (4, ry);
|
||||
found = View.FindDeepestView (top, 15, 4);
|
||||
Assert.Equal (top, found);
|
||||
//Assert.Equal (15, found.FrameToScreen ().X);
|
||||
//Assert.Equal (4, found.FrameToScreen ().Y);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1004,30 +1015,31 @@ public class NavigationTests
|
||||
top.BoundsToScreen (-4, -3, out col, out row);
|
||||
Assert.Equal (0, col);
|
||||
Assert.Equal (0, row);
|
||||
Assert.Null (View.FindDeepestView (top, -4, -3, out int rx, out int ry));
|
||||
Assert.Equal (0, rx);
|
||||
Assert.Equal (0, ry);
|
||||
var found = View.FindDeepestView (top, -4, -3);
|
||||
Assert.Null (found);
|
||||
//Assert.Equal (0, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
Assert.Equal (Point.Empty, top.ScreenToFrame (3, 2));
|
||||
top.BoundsToScreen (0, 0, out col, out row);
|
||||
Assert.Equal (4, col);
|
||||
Assert.Equal (3, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 3, 2, out rx, out ry));
|
||||
Assert.Equal (0, rx);
|
||||
Assert.Equal (0, ry);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 3, 2));
|
||||
//Assert.Equal (0, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (10, 0), top.ScreenToFrame (13, 2));
|
||||
top.BoundsToScreen (10, 0, out col, out row);
|
||||
Assert.Equal (14, col);
|
||||
Assert.Equal (3, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 13, 2, out rx, out ry));
|
||||
Assert.Equal (10, rx);
|
||||
Assert.Equal (0, ry);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 13, 2));
|
||||
//Assert.Equal (10, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (11, 1), top.ScreenToFrame (14, 3));
|
||||
top.BoundsToScreen (11, 1, out col, out row);
|
||||
Assert.Equal (15, col);
|
||||
Assert.Equal (4, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 14, 3, out rx, out ry));
|
||||
Assert.Equal (11, rx);
|
||||
Assert.Equal (1, ry);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 14, 3));
|
||||
//Assert.Equal (11, found.FrameToScreen ().X);
|
||||
//Assert.Equal (1, found.FrameToScreen ().Y);
|
||||
|
||||
// view
|
||||
Assert.Equal (new Point (-7, -5), view.ScreenToFrame (0, 0));
|
||||
@@ -1043,44 +1055,44 @@ public class NavigationTests
|
||||
view.BoundsToScreen (-6, -4, out col, out row);
|
||||
Assert.Equal (1, col);
|
||||
Assert.Equal (1, row);
|
||||
Assert.Null (View.FindDeepestView (top, 1, 1, out rx, out ry));
|
||||
Assert.Equal (0, rx);
|
||||
Assert.Equal (0, ry);
|
||||
Assert.Null (View.FindDeepestView (top, 1, 1));
|
||||
//Assert.Equal (0, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (-4, -3), view.ScreenToFrame (3, 2));
|
||||
view.BoundsToScreen (-3, -2, out col, out row);
|
||||
Assert.Equal (4, col);
|
||||
Assert.Equal (3, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 4, 3, out rx, out ry));
|
||||
Assert.Equal (1, rx);
|
||||
Assert.Equal (1, ry);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 4, 3));
|
||||
//Assert.Equal (1, found.FrameToScreen ().X);
|
||||
//Assert.Equal (1, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (-1, -1), view.ScreenToFrame (6, 4));
|
||||
view.BoundsToScreen (0, 0, out col, out row);
|
||||
Assert.Equal (7, col);
|
||||
Assert.Equal (5, row);
|
||||
Assert.Equal (view, View.FindDeepestView (top, 7, 5, out rx, out ry));
|
||||
Assert.Equal (0, rx);
|
||||
Assert.Equal (0, ry);
|
||||
Assert.Equal (view, View.FindDeepestView (top, 7, 5));
|
||||
//Assert.Equal (0, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (6, -1), view.ScreenToFrame (13, 4));
|
||||
view.BoundsToScreen (7, 0, out col, out row);
|
||||
Assert.Equal (14, col);
|
||||
Assert.Equal (5, row);
|
||||
Assert.Equal (view, View.FindDeepestView (top, 14, 5, out rx, out ry));
|
||||
Assert.Equal (7, rx);
|
||||
Assert.Equal (0, ry);
|
||||
Assert.Equal (view, View.FindDeepestView (top, 14, 5));
|
||||
//Assert.Equal (7, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (7, -2), view.ScreenToFrame (14, 3));
|
||||
view.BoundsToScreen (8, -1, out col, out row);
|
||||
Assert.Equal (15, col);
|
||||
Assert.Equal (4, row);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 15, 4, out rx, out ry));
|
||||
Assert.Equal (12, rx);
|
||||
Assert.Equal (2, ry);
|
||||
Assert.Equal (top, View.FindDeepestView (top, 15, 4));
|
||||
//Assert.Equal (12, found.FrameToScreen ().X);
|
||||
//Assert.Equal (2, found.FrameToScreen ().Y);
|
||||
Assert.Equal (new Point (16, -2), view.ScreenToFrame (23, 3));
|
||||
view.BoundsToScreen (17, -1, out col, out row);
|
||||
Assert.Equal (24, col);
|
||||
Assert.Equal (4, row);
|
||||
Assert.Null (View.FindDeepestView (top, 24, 4, out rx, out ry));
|
||||
Assert.Equal (0, rx);
|
||||
Assert.Equal (0, ry);
|
||||
Assert.Null (View.FindDeepestView (top, 24, 4));
|
||||
//Assert.Equal (0, found.FrameToScreen ().X);
|
||||
//Assert.Equal (0, found.FrameToScreen ().Y);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -518,7 +518,7 @@ public class MenuBarTests
|
||||
|
||||
Application.OnMouseEvent (
|
||||
new MouseEventEventArgs (
|
||||
new MouseEvent { X = 20, Y = 4, Flags = MouseFlags.Button1Clicked }
|
||||
new MouseEvent { X = 20, Y = 5, Flags = MouseFlags.Button1Clicked }
|
||||
)
|
||||
);
|
||||
|
||||
@@ -555,7 +555,7 @@ public class MenuBarTests
|
||||
|
||||
Application.OnMouseEvent (
|
||||
new MouseEventEventArgs (
|
||||
new MouseEvent { X = 20, Y = 4 + i, Flags = MouseFlags.Button1Clicked }
|
||||
new MouseEvent { X = 20, Y = 5 + i, Flags = MouseFlags.Button1Clicked }
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user