diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs
index 1afa0994b..1c9f39d27 100644
--- a/Terminal.Gui/View/Layout/ViewLayout.cs
+++ b/Terminal.Gui/View/Layout/ViewLayout.cs
@@ -689,6 +689,36 @@ public partial class View
#nullable restore
+ ///
+ /// Determines if this view is currently visible in the visible area,
+ /// given the passed x and y location coordinates.
+ ///
+ /// The x location.
+ /// The y location.
+ /// if it's visible, otherwise.
+ public bool IsViewLocationVisibleInViewport (int x, int y)
+ {
+ Rectangle thisFrame = ViewportToScreen (Viewport);
+ Rectangle thisOffset = ViewportToScreen (new (new (x, y), new (Viewport.Width, Viewport.Height )));
+ View view;
+
+ if (Application.Current is { })
+ {
+ view = FindDeepestView (Application.Current, thisOffset.X, thisOffset.Y);
+ }
+ else if (SuperView is { })
+ {
+ Rectangle containerFrame = SuperView.ViewportToScreen (SuperView.Viewport);
+ view = containerFrame.IntersectsWith (thisOffset) ? this : null;
+ }
+ else
+ {
+ view = thisFrame.IntersectsWith (thisOffset) ? this : null;
+ }
+
+ return view == this;
+ }
+
///
/// Gets a new location of the that is within the Viewport of the 's
/// (e.g. for dragging a Window). The `out` parameters are the new X and Y coordinates.
diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
index 2fbc724e4..07c010104 100644
--- a/Terminal.Gui/Views/TextField.cs
+++ b/Terminal.Gui/Views/TextField.cs
@@ -1196,10 +1196,10 @@ public class TextField : View
int pos = _cursorPosition - ScrollOffset + Math.Min (Frame.X, 0);
int offB = OffSetBackground ();
- var thisOffset = ViewportToScreen (new (new (col, Viewport.Y), new (Viewport.Width - col, Viewport.Height - Viewport.Y)));
- var view = Application.Current is { } ? FindDeepestView (Application.Current, thisOffset.X, thisOffset.Y) : this;
+ bool isVisibleInViewport = IsViewLocationVisibleInViewport (col, Viewport.Y);
- if (view == this && pos > -1
+ if (isVisibleInViewport
+ && pos > -1
&& col >= pos
&& pos < Frame.Width + offB)
{
diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs
index f08784fe8..a8d05888e 100644
--- a/Terminal.Gui/Views/TextView.cs
+++ b/Terminal.Gui/Views/TextView.cs
@@ -3888,10 +3888,9 @@ public class TextView : View
int posX = CurrentColumn - _leftColumn;
int posY = CurrentRow - _topRow;
- var thisOffset = ViewportToScreen (new (new (col, posY), new (Viewport.Width - col, Viewport.Height - posY)));
- var view = Application.Current is { } ? FindDeepestView (Application.Current, thisOffset.X, thisOffset.Y) : this;
+ bool isVisibleInViewport = IsViewLocationVisibleInViewport (col, posY);
- if (view == this && posX > -1 && col >= posX && posX < Frame.Width - RightOffset && _topRow <= CurrentRow && posY < Frame.Height - BottomOffset)
+ if (isVisibleInViewport && posX > -1 && col >= posX && posX < Frame.Width - RightOffset && _topRow <= CurrentRow && posY < Frame.Height - BottomOffset)
{
ResetCursorVisibility ();
Move (col, CurrentRow - _topRow);
diff --git a/UnitTests/View/Layout/ViewportTests.cs b/UnitTests/View/Layout/ViewportTests.cs
index 8d9035359..1c9ba4580 100644
--- a/UnitTests/View/Layout/ViewportTests.cs
+++ b/UnitTests/View/Layout/ViewportTests.cs
@@ -359,4 +359,94 @@ public class ViewportTests (ITestOutputHelper output)
Assert.Equal (view.Viewport.Size, view.ContentSize);
}
+ [Theory]
+ [InlineData (0, 0, true)]
+ [InlineData (-1, 0, true)]
+ [InlineData (0, -1, true)]
+ [InlineData (-1, -1, true)]
+ [InlineData (-2, -2, true)]
+ [InlineData (-3, -3, true)]
+ [InlineData (-4, -4, true)]
+ [InlineData (-5, -4, false)]
+ [InlineData (-4, -5, false)]
+ [InlineData (-5, -5, false)]
+
+ [InlineData (1, 1, true)]
+ [InlineData (2, 2, true)]
+ [InlineData (3, 3, true)]
+ [InlineData (4, 4, true)]
+ [InlineData (5, 4, false)]
+ [InlineData (4, 5, false)]
+ [InlineData (5, 5, false)]
+ public void IsViewVisibleInViewport_No_Driver_No_SuperView (int x, int y, bool expected)
+ {
+ var view = new View { X = 1, Y = 1, Width = 5, Height = 5 };
+ Assert.True (view.IsViewLocationVisibleInViewport (x, y) == expected);
+ }
+
+ [Theory]
+ [InlineData (0, 0, true)]
+ [InlineData (-1, 0, true)]
+ [InlineData (0, -1, true)]
+ [InlineData (-1, -1, true)]
+ [InlineData (-2, -2, true)]
+ [InlineData (-3, -3, true)]
+ [InlineData (-4, -4, true)]
+ [InlineData (-5, -4, true)]
+ [InlineData (-4, -5, true)]
+ [InlineData (-5, -5, true)]
+ [InlineData (-6, -5, false)]
+ [InlineData (-5, -6, false)]
+ [InlineData (-6, -6, false)]
+
+ [InlineData (1, 1, true)]
+ [InlineData (2, 2, true)]
+ [InlineData (3, 3, true)]
+ [InlineData (4, 4, true)]
+ [InlineData (5, 4, true)]
+ [InlineData (4, 5, true)]
+ [InlineData (5, 5, true)]
+ [InlineData (6, 5, true)]
+ [InlineData (6, 6, true)]
+ [InlineData (7, 7, true)]
+ [InlineData (8, 8, true)]
+ [InlineData (9, 8, false)]
+ [InlineData (8, 9, false)]
+ [InlineData (9, 9, false)]
+ public void IsViewVisibleInViewport_No_Driver_With_SuperView (int x, int y, bool expected)
+ {
+ var view = new View { X = 1, Y = 1, Width = 5, Height = 5 };
+ var top = new Toplevel { Width = 10, Height = 10 };
+ top.Add (view);
+
+ Assert.True (view.IsViewLocationVisibleInViewport (x, y) == expected);
+ }
+
+ [SetupFakeDriver]
+ [Theory]
+ [InlineData (0, 0, true)]
+ [InlineData (-1, 0, false)]
+ [InlineData (0, -1, false)]
+ [InlineData (-1, -1, false)]
+
+ [InlineData (1, 0, true)]
+ [InlineData (0, 1, true)]
+ [InlineData (1, 1, true)]
+ [InlineData (2, 2, true)]
+ [InlineData (3, 3, true)]
+ [InlineData (4, 4, true)]
+ [InlineData (5, 4, false)]
+ [InlineData (4, 5, false)]
+ [InlineData (5, 5, false)]
+ public void IsViewVisibleInViewport_With_Driver (int x, int y, bool expected)
+ {
+ ((FakeDriver)Application.Driver).SetBufferSize (10, 10);
+
+ var view = new View { X = 1, Y = 1, Width = 5, Height = 5 };
+ var top = new Toplevel ();
+ top.Add (view);
+ Application.Begin (top);
+
+ Assert.True (view.IsViewLocationVisibleInViewport (x, y) == expected);
+ }
}