From 46fcb58a7107d254fce7a2302f9eea52a8b211e1 Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 25 Apr 2024 23:30:08 +0100 Subject: [PATCH] Create IsViewLocationVisibleInViewport method. --- Terminal.Gui/View/Layout/ViewLayout.cs | 30 +++++++++ Terminal.Gui/Views/TextField.cs | 6 +- Terminal.Gui/Views/TextView.cs | 5 +- UnitTests/View/Layout/ViewportTests.cs | 90 ++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 6 deletions(-) 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); + } }