From 75c0160d96e22f69e36c05697b16a915c65bfd59 Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 15 Sep 2022 01:18:00 +0000 Subject: [PATCH] Fixes #1999. Prevents the mouseGrabView being executed with a null view. (#2000) * Unit test that will fail without the fix. * ScrollView must return true after ungrab the mouse to allow the View property run after returned. * Fixes #1999. Prevents the mouseGrabView being executed with a null view. * Added one more assert null check. --- Terminal.Gui/Core/Application.cs | 9 +++- Terminal.Gui/Views/ScrollView.cs | 1 - UnitTests/ApplicationTests.cs | 82 ++++++++++++++++++++++++++++++++ UnitTests/ReflectionTools.cs | 35 ++++++++++++++ 4 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 UnitTests/ReflectionTools.cs diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 81f28ee33..2632c69be 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -638,6 +638,11 @@ namespace Terminal.Gui { } RootMouseEvent?.Invoke (me); if (mouseGrabView != null) { + if (view == null) { + UngrabMouse (); + return; + } + var newxy = mouseGrabView.ScreenToView (me.X, me.Y); var nme = new MouseEvent () { X = newxy.X, @@ -653,7 +658,9 @@ namespace Terminal.Gui { // System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}"); if (mouseGrabView != null) { mouseGrabView.OnMouseEvent (nme); - return; + if (mouseGrabView != null) { + return; + } } } diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index a77a8f2aa..306261dae 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -517,7 +517,6 @@ namespace Terminal.Gui { horizontal.MouseEvent (me); } else if (IsOverridden (me.View)) { Application.UngrabMouse (); - return false; } return true; } diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs index 5f582535e..1122bce23 100644 --- a/UnitTests/ApplicationTests.cs +++ b/UnitTests/ApplicationTests.cs @@ -1408,5 +1408,87 @@ namespace Terminal.Gui.Core { Application.Shutdown (); } + + [Fact, AutoInitShutdown] + public void MouseGrabView_WithNullMouseEventView () + { + var tf = new TextField () { Width = 10 }; + var sv = new ScrollView () { + Width = Dim.Fill (), + Height = Dim.Fill (), + ContentSize = new Size (100, 100) + }; + + sv.Add (tf); + Application.Top.Add (sv); + + var iterations = -1; + + Application.Iteration = () => { + iterations++; + if (iterations == 0) { + Assert.True (tf.HasFocus); + Assert.Null (Application.mouseGrabView); + + ReflectionTools.InvokePrivate ( + typeof (Application), + "ProcessMouseEvent", + new MouseEvent () { + X = 5, + Y = 5, + Flags = MouseFlags.ReportMousePosition + }); + + Assert.Equal (sv, Application.mouseGrabView); + + MessageBox.Query ("Title", "Test", "Ok"); + + Assert.Null (Application.mouseGrabView); + } else if (iterations == 1) { + Assert.Equal (sv, Application.mouseGrabView); + + ReflectionTools.InvokePrivate ( + typeof (Application), + "ProcessMouseEvent", + new MouseEvent () { + X = 5, + Y = 5, + Flags = MouseFlags.ReportMousePosition + }); + + Assert.Null (Application.mouseGrabView); + + ReflectionTools.InvokePrivate ( + typeof (Application), + "ProcessMouseEvent", + new MouseEvent () { + X = 40, + Y = 12, + Flags = MouseFlags.ReportMousePosition + }); + + Assert.Null (Application.mouseGrabView); + + ReflectionTools.InvokePrivate ( + typeof (Application), + "ProcessMouseEvent", + new MouseEvent () { + X = 0, + Y = 0, + Flags = MouseFlags.Button1Pressed + }); + + Assert.Null (Application.mouseGrabView); + + Application.RequestStop (); + } else if (iterations == 2) { + Assert.Null (Application.mouseGrabView); + + Application.RequestStop (); + } + }; + + Application.Run (); + } } } diff --git a/UnitTests/ReflectionTools.cs b/UnitTests/ReflectionTools.cs new file mode 100644 index 000000000..3f661bc5f --- /dev/null +++ b/UnitTests/ReflectionTools.cs @@ -0,0 +1,35 @@ +using System; +using System.Reflection; + +public static class ReflectionTools { + // If the class is non-static + public static Object InvokePrivate (Object objectUnderTest, string method, params object [] args) + { + Type t = objectUnderTest.GetType (); + return t.InvokeMember (method, + BindingFlags.InvokeMethod | + BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.Static, + null, + objectUnderTest, + args); + } + // if the class is static + public static Object InvokePrivate (Type typeOfObjectUnderTest, string method, params object [] args) + { + MemberInfo [] members = typeOfObjectUnderTest.GetMembers (BindingFlags.NonPublic | BindingFlags.Static); + foreach (var member in members) { + if (member.Name == method) { + return typeOfObjectUnderTest.InvokeMember (method, + BindingFlags.NonPublic | + BindingFlags.Static | + BindingFlags.InvokeMethod, + null, + typeOfObjectUnderTest, + args); + } + } + return null; + } +}