diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index eb1200999f..f0764905c4 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -581,7 +581,22 @@ static View FindTopFromView (View view) return top; } - internal static View mouseGrabView; + static View mouseGrabView; + + /// + /// The view that grabbed the mouse, to where will be routed all the mouse events. + /// + public static View MouseGrabView => mouseGrabView; + + /// + /// Event to be invoked when a view grab the mouse. + /// + public static event Action GrabbedMouse; + + /// + /// Event to be invoked when a view ungrab the mouse. + /// + public static event Action UnGrabbedMouse; /// /// Grabs the mouse, forcing all mouse events to be routed to the specified view until UngrabMouse is called. @@ -592,6 +607,7 @@ public static void GrabMouse (View view) { if (view == null) return; + OnGrabbedMouse (view); mouseGrabView = view; Driver.UncookMouse (); } @@ -601,10 +617,27 @@ public static void GrabMouse (View view) /// public static void UngrabMouse () { + if (mouseGrabView == null) + return; + OnUnGrabbedMouse (mouseGrabView); mouseGrabView = null; Driver.CookMouse (); } + static void OnGrabbedMouse (View view) + { + if (view == null) + return; + GrabbedMouse?.Invoke (view); + } + + static void OnUnGrabbedMouse (View view) + { + if (view == null) + return; + UnGrabbedMouse?.Invoke (view); + } + /// /// Merely a debugging aid to see the raw mouse events /// @@ -656,7 +689,7 @@ static void ProcessMouseEvent (MouseEvent me) lastMouseOwnerView?.OnMouseLeave (me); } // System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}"); - if (mouseGrabView != null && mouseGrabView.OnMouseEvent (nme)) { + if (mouseGrabView?.OnMouseEvent (nme) == true) { return; } } diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index e40d852b5b..804ddb5ed3 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -1816,7 +1816,7 @@ public override bool MouseEvent (MouseEvent me) internal bool HandleGrabView (MouseEvent me, View current) { - if (Application.mouseGrabView != null) { + if (Application.MouseGrabView != null) { if (me.View is MenuBar || me.View is Menu) { var mbar = GetMouseGrabViewInstance (me.View); if (mbar != null) { @@ -1890,7 +1890,7 @@ internal bool HandleGrabView (MouseEvent me, View current) //if (!(me.View is MenuBar) && !(me.View is Menu) && me.Flags != MouseFlags.Button1Pressed)) // return false; - //if (Application.mouseGrabView != null) { + //if (Application.MouseGrabView != null) { // if (me.View is MenuBar || me.View is Menu) { // me.X -= me.OfX; // me.Y -= me.OfY; @@ -1905,8 +1905,8 @@ internal bool HandleGrabView (MouseEvent me, View current) // return true; //} - //if (Application.mouseGrabView != null) { - // if (Application.mouseGrabView == me.View && me.View == current) { + //if (Application.MouseGrabView != null) { + // if (Application.MouseGrabView == me.View && me.View == current) { // me.X -= me.OfX; // me.Y -= me.OfY; // } else if (me.View != current && me.View is MenuBar && me.View is Menu) { @@ -1927,7 +1927,7 @@ internal bool HandleGrabView (MouseEvent me, View current) MenuBar GetMouseGrabViewInstance (View view) { - if (view == null || Application.mouseGrabView == null) { + if (view == null || Application.MouseGrabView == null) { return null; } @@ -1938,7 +1938,7 @@ MenuBar GetMouseGrabViewInstance (View view) hostView = ((Menu)view).host; } - var grabView = Application.mouseGrabView; + var grabView = Application.MouseGrabView; MenuBar hostGrabView = null; if (grabView is MenuBar) { hostGrabView = (MenuBar)grabView; diff --git a/Terminal.Gui/Views/ScrollBarView.cs b/Terminal.Gui/Views/ScrollBarView.cs index f584a2ec7d..80c5b6ee37 100644 --- a/Terminal.Gui/Views/ScrollBarView.cs +++ b/Terminal.Gui/Views/ScrollBarView.cs @@ -357,7 +357,7 @@ void ShowHideScrollBars (bool redraw = true) } else if (otherScrollBarView != null && otherScrollBarView.contentBottomRightCorner != null) { otherScrollBarView.contentBottomRightCorner.Visible = false; } - if (Application.mouseGrabView != null && Application.mouseGrabView == this) { + if (Application.MouseGrabView != null && Application.MouseGrabView == this) { Application.UngrabMouse (); } } else if (contentBottomRightCorner != null) { @@ -638,9 +638,9 @@ public override bool MouseEvent (MouseEvent me) var pos = Position; if (me.Flags != MouseFlags.Button1Released - && (Application.mouseGrabView == null || Application.mouseGrabView != this)) { + && (Application.MouseGrabView == null || Application.MouseGrabView != this)) { Application.GrabMouse (this); - } else if (me.Flags == MouseFlags.Button1Released && Application.mouseGrabView != null && Application.mouseGrabView == this) { + } else if (me.Flags == MouseFlags.Button1Released && Application.MouseGrabView != null && Application.MouseGrabView == this) { lastLocation = -1; Application.UngrabMouse (); return true; diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index 306261dae3..8202751786 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -227,7 +227,7 @@ public override void Add (View view) void View_MouseLeave (MouseEventArgs e) { - if (Application.mouseGrabView != null && Application.mouseGrabView != vertical && Application.mouseGrabView != horizontal) { + if (Application.MouseGrabView != null && Application.MouseGrabView != vertical && Application.MouseGrabView != horizontal) { Application.UngrabMouse (); } } diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 3f9ae85f01..5852c7051b 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -249,9 +249,9 @@ void TextField_Initialized (object sender, EventArgs e) /// public override bool OnLeave (View view) { - if (Application.mouseGrabView != null && Application.mouseGrabView == this) + if (Application.MouseGrabView != null && Application.MouseGrabView == this) Application.UngrabMouse (); - //if (SelectedLength != 0 && !(Application.mouseGrabView is MenuBar)) + //if (SelectedLength != 0 && !(Application.MouseGrabView is MenuBar)) // ClearAllSelection (); return base.OnLeave (view); @@ -1049,7 +1049,7 @@ public override bool MouseEvent (MouseEvent ev) int x = PositionCursor (ev); isButtonReleased = false; PrepareSelection (x); - if (Application.mouseGrabView == null) { + if (Application.MouseGrabView == null) { Application.GrabMouse (this); } } else if (ev.Flags == MouseFlags.Button1Released) { diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index f671a5264f..edbdb8bd3f 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -4328,7 +4328,7 @@ public override bool MouseEvent (MouseEvent ev) } lastWasKill = false; columnTrack = currentColumn; - if (Application.mouseGrabView == null) { + if (Application.MouseGrabView == null) { Application.GrabMouse (this); } } else if (ev.Flags.HasFlag (MouseFlags.Button1Released)) { @@ -4407,7 +4407,7 @@ void ProcessMouseClick (MouseEvent ev, out List line) /// public override bool OnLeave (View view) { - if (Application.mouseGrabView != null && Application.mouseGrabView == this) { + if (Application.MouseGrabView != null && Application.MouseGrabView == this) { Application.UngrabMouse (); } diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs index 56fdb9223f..e0f331e747 100644 --- a/UnitTests/ApplicationTests.cs +++ b/UnitTests/ApplicationTests.cs @@ -1144,7 +1144,7 @@ public void Internal_Tests () Assert.NotNull (Application.Top); var rs = Application.Begin (Application.Top); Assert.Equal (Application.Top, rs.Toplevel); - Assert.Null (Application.mouseGrabView); + Assert.Null (Application.MouseGrabView); Assert.Null (Application.WantContinuousButtonPressedView); Assert.False (Application.DebugDrawBounds); Assert.False (Application.ShowChild (Application.Top)); @@ -1428,7 +1428,7 @@ public void MouseGrabView_WithNullMouseEventView () iterations++; if (iterations == 0) { Assert.True (tf.HasFocus); - Assert.Null (Application.mouseGrabView); + Assert.Null (Application.MouseGrabView); ReflectionTools.InvokePrivate ( typeof (Application), @@ -1439,13 +1439,13 @@ public void MouseGrabView_WithNullMouseEventView () Flags = MouseFlags.ReportMousePosition }); - Assert.Equal (sv, Application.mouseGrabView); + Assert.Equal (sv, Application.MouseGrabView); MessageBox.Query ("Title", "Test", "Ok"); - Assert.Null (Application.mouseGrabView); + Assert.Null (Application.MouseGrabView); } else if (iterations == 1) { - Assert.Equal (sv, Application.mouseGrabView); + Assert.Equal (sv, Application.MouseGrabView); ReflectionTools.InvokePrivate ( typeof (Application), @@ -1456,7 +1456,7 @@ public void MouseGrabView_WithNullMouseEventView () Flags = MouseFlags.ReportMousePosition }); - Assert.Null (Application.mouseGrabView); + Assert.Null (Application.MouseGrabView); ReflectionTools.InvokePrivate ( typeof (Application), @@ -1467,7 +1467,7 @@ public void MouseGrabView_WithNullMouseEventView () Flags = MouseFlags.ReportMousePosition }); - Assert.Null (Application.mouseGrabView); + Assert.Null (Application.MouseGrabView); ReflectionTools.InvokePrivate ( typeof (Application), @@ -1478,11 +1478,11 @@ public void MouseGrabView_WithNullMouseEventView () Flags = MouseFlags.Button1Pressed }); - Assert.Null (Application.mouseGrabView); + Assert.Null (Application.MouseGrabView); Application.RequestStop (); } else if (iterations == 2) { - Assert.Null (Application.mouseGrabView); + Assert.Null (Application.MouseGrabView); Application.RequestStop (); } @@ -1490,5 +1490,68 @@ public void MouseGrabView_WithNullMouseEventView () Application.Run (); } + + [Fact, AutoInitShutdown] + public void MouseGrabView_GrabbedMouse_UnGrabbedMouse () + { + View grabView = null; + var count = 0; + + var view1 = new View (); + var view2 = new View (); + + Application.GrabbedMouse += Application_GrabbedMouse; + Application.UnGrabbedMouse += Application_UnGrabbedMouse; + + Application.GrabMouse (view1); + Assert.Equal (0, count); + Assert.Equal (grabView, view1); + Assert.Equal (view1, Application.MouseGrabView); + + Application.UngrabMouse (); + Assert.Equal (1, count); + Assert.Equal (grabView, view1); + Assert.Null (Application.MouseGrabView); + + Application.GrabbedMouse += Application_GrabbedMouse; + Application.UnGrabbedMouse += Application_UnGrabbedMouse; + + Application.GrabMouse (view2); + Assert.Equal (1, count); + Assert.Equal (grabView, view2); + Assert.Equal (view2, Application.MouseGrabView); + + Application.UngrabMouse (); + Assert.Equal (2, count); + Assert.Equal (grabView, view2); + Assert.Null (Application.MouseGrabView); + + void Application_GrabbedMouse (View obj) + { + if (count == 0) { + Assert.Equal (view1, obj); + grabView = view1; + } else { + Assert.Equal (view2, obj); + grabView = view2; + } + + Application.GrabbedMouse -= Application_GrabbedMouse; + } + + void Application_UnGrabbedMouse (View obj) + { + if (count == 0) { + Assert.Equal (view1, obj); + Assert.Equal (grabView, obj); + } else { + Assert.Equal (view2, obj); + Assert.Equal (grabView, obj); + } + count++; + + Application.UnGrabbedMouse -= Application_UnGrabbedMouse; + } + } } } diff --git a/UnitTests/ContextMenuTests.cs b/UnitTests/ContextMenuTests.cs index 5df3b07ce2..a0c88168ae 100644 --- a/UnitTests/ContextMenuTests.cs +++ b/UnitTests/ContextMenuTests.cs @@ -519,38 +519,38 @@ public void ContextMenu_Is_Closed_If_Another_MenuBar_Is_Open_Or_Vice_Versa () Application.Top.Add (menu); - Assert.Null (Application.mouseGrabView); + Assert.Null (Application.MouseGrabView); cm.Show (); Assert.True (ContextMenu.IsShow); - Assert.Equal (cm.MenuBar, Application.mouseGrabView); + Assert.Equal (cm.MenuBar, Application.MouseGrabView); Assert.False (menu.IsMenuOpen); Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, new KeyModifiers ()))); Assert.False (ContextMenu.IsShow); - Assert.Equal (menu, Application.mouseGrabView); + Assert.Equal (menu, Application.MouseGrabView); Assert.True (menu.IsMenuOpen); cm.Show (); Assert.True (ContextMenu.IsShow); - Assert.Equal (cm.MenuBar, Application.mouseGrabView); + Assert.Equal (cm.MenuBar, Application.MouseGrabView); Assert.False (menu.IsMenuOpen); Assert.False (menu.OnKeyDown (new KeyEvent (Key.Null, new KeyModifiers () { Alt = true }))); Assert.True (menu.OnKeyUp (new KeyEvent (Key.Null, new KeyModifiers () { Alt = true }))); Assert.False (ContextMenu.IsShow); - Assert.Equal (menu, Application.mouseGrabView); + Assert.Equal (menu, Application.MouseGrabView); Assert.True (menu.IsMenuOpen); cm.Show (); Assert.True (ContextMenu.IsShow); - Assert.Equal (cm.MenuBar, Application.mouseGrabView); + Assert.Equal (cm.MenuBar, Application.MouseGrabView); Assert.False (menu.IsMenuOpen); Assert.False (menu.MouseEvent (new MouseEvent () { X = 1, Flags = MouseFlags.ReportMousePosition, View = menu })); Assert.True (ContextMenu.IsShow); - Assert.Equal (cm.MenuBar, Application.mouseGrabView); + Assert.Equal (cm.MenuBar, Application.MouseGrabView); Assert.False (menu.IsMenuOpen); Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Flags = MouseFlags.Button1Clicked, View = menu })); Assert.False (ContextMenu.IsShow); - Assert.Equal (menu, Application.mouseGrabView); + Assert.Equal (menu, Application.MouseGrabView); Assert.True (menu.IsMenuOpen); } diff --git a/UnitTests/ScrollBarViewTests.cs b/UnitTests/ScrollBarViewTests.cs index 12215d1540..0248674bf8 100644 --- a/UnitTests/ScrollBarViewTests.cs +++ b/UnitTests/ScrollBarViewTests.cs @@ -947,7 +947,7 @@ This is a test Flags = MouseFlags.Button1Clicked }); - Assert.Null (Application.mouseGrabView); + Assert.Null (Application.MouseGrabView); Assert.True (clicked); clicked = false; @@ -974,7 +974,7 @@ This is a test Flags = MouseFlags.Button1Clicked }); - Assert.Null (Application.mouseGrabView); + Assert.Null (Application.MouseGrabView); Assert.True (clicked); Assert.Equal (5, sbv.Size); Assert.False (sbv.ShowScrollIndicator);