Refactored Enter event and added unit tests

This commit is contained in:
Tig
2024-09-21 15:46:05 -06:00
parent 1dd2d9f38c
commit e95ff61fef
12 changed files with 923 additions and 642 deletions

View File

@@ -1,4 +1,5 @@
#nullable enable
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
@@ -344,20 +345,26 @@ public static partial class Application // Mouse handling
_cachedViewsUnderMouse.Add (view);
if (view is Adornment adornmentView)
bool raise = false;
if (view is Adornment { Parent: { } } adornmentView)
{
Point frameLoc = view.ScreenToFrame (me.ScreenPosition);
if (adornmentView.Parent is { } && !adornmentView.Contains (frameLoc))
{
view.NewMouseEnterEvent (me);
}
raise = adornmentView.Contains (frameLoc);
}
else
{
Point superViewLoc = view.SuperView?.ScreenToViewport (me.ScreenPosition) ?? me.ScreenPosition;
if (view.Contains (superViewLoc))
raise = view.Contains (superViewLoc);
}
if (raise)
{
CancelEventArgs eventArgs = new ();
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
if (cancelled is true || eventArgs.Cancel)
{
view.NewMouseEnterEvent (me);
break;
}
}
}

View File

@@ -1,4 +1,5 @@
#nullable enable
using System.ComponentModel;
using Terminal.Gui;
using Attribute = Terminal.Gui.Attribute;
@@ -227,37 +228,5 @@ public class Adornment : View
return Thickness.Contains (frame, location);
}
/// <inheritdoc/>
protected internal override bool? OnMouseEnter (MouseEvent mouseEvent)
{
//// Invert Normal
//if (Diagnostics.HasFlag (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
//{
// var cs = new ColorScheme (ColorScheme)
// {
// Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
// };
// ColorScheme = cs;
//}
return base.OnMouseEnter (mouseEvent);
}
/// <inheritdoc/>
protected internal override bool OnMouseLeave (MouseEvent mouseEvent)
{
//// Invert Normal
//if (Diagnostics.FastHasFlags (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
//{
// var cs = new ColorScheme (ColorScheme)
// {
// Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
// };
// ColorScheme = cs;
//}
return base.OnMouseLeave (mouseEvent);
}
#endregion Mouse Support
}

View File

@@ -31,9 +31,6 @@ public partial class View // Mouse APIs
/// </remarks>
public event EventHandler<MouseEventEventArgs>? MouseClick;
/// <summary>Event fired when the mouse moves into the View's <see cref="Viewport"/>.</summary>
public event EventHandler<MouseEventEventArgs>? MouseEnter;
/// <summary>Event fired when a mouse event occurs.</summary>
/// <remarks>
/// <para>
@@ -140,25 +137,100 @@ public partial class View // Mouse APIs
/// <value><see langword="true"/> if mouse position reports are wanted; otherwise, <see langword="false"/>.</value>
public virtual bool WantMousePositionReports { get; set; }
#region MouseEnterLeave
/// <summary>
/// Called by <see cref="NewMouseEvent"/> when the mouse enters <see cref="Viewport"/>. The view will
/// then receive mouse events until <see cref="OnMouseLeave"/> is called indicating the mouse has left
/// the view.
/// INTERNAL Called by <see cref="Application.OnMouseEvent"/> when the mouse moves over the View's <see cref="Frame"/>.
/// <see cref="MouseLeave"/> will
/// be raised when the mouse is no longer over the <see cref="Frame"/>. If another View occludes this View, the
/// that View will also receive MouseEnter/Leave events.
/// </summary>
/// <param name="eventArgs"></param>
/// <returns>
/// <see langword="true"/> if the event was canceled, <see langword="false"/> if not, <see langword="null"/> if the
/// view is not visible. Cancelling the event
/// prevents Views higher in the visible hierarchy from receiving Enter/Leave events.
/// </returns>
internal bool? NewMouseEnterEvent (CancelEventArgs eventArgs)
{
if (!CanBeVisible (this))
{
return null;
}
if (OnMouseEnter (eventArgs))
{
return true;
}
MouseEnter?.Invoke (this, eventArgs);
#if HOVER
if (HighlightStyle.HasFlag(HighlightStyle.Hover))
{
if (SetHighlight (HighlightStyle.Hover))
{
return true;
}
}
#endif
return eventArgs.Cancel;
}
/// <summary>
/// Called when the mouse moves over the View's <see cref="Frame"/> and no other non-Subview occludes it. <see cref="MouseLeave"/> will
/// be raised when the mouse is no longer over the <see cref="Frame"/>.
/// </summary>
/// <remarks>
/// <para>
/// Override this method or subscribe to <see cref="MouseEnter"/> to change the default enter behavior.
/// A view must be visible to receive Enter events (Leave events are always received).
/// </para>
/// <para>
/// The coordinates are relative to <see cref="View.Viewport"/>.
/// If the event is cancelled, the mouse event will not be propagated to other views and <see cref="MouseEnter"/>
/// will not be raised.
/// </para>
/// <para>
/// Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
/// </para>
/// <para>
/// See <see cref="SetHighlight"/> for more information.
/// </para>
/// </remarks>
/// <param name="mouseEvent"></param>
/// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
protected internal virtual bool? OnMouseEnter (MouseEvent mouseEvent)
{
return false;
}
/// <param name="eventArgs"></param>
/// <returns>
/// <see langword="true"/> if the event was canceled, <see langword="false"/> if not. Cancelling the event
/// prevents Views higher in the visible hierarchy from receiving Enter/Leave events.
/// </returns>
protected virtual bool OnMouseEnter (CancelEventArgs eventArgs) { return false; }
/// <summary>
/// Raised when the mouse moves over the View's <see cref="Frame"/>. <see cref="MouseLeave"/> will
/// be raised when the mouse is no longer over the <see cref="Frame"/>. If another View occludes this View, the
/// that View will also receive MouseEnter/Leave events.
/// </summary>
/// <remarks>
/// <para>
/// A view must be visible to receive Enter events (Leave events are always received).
/// </para>
/// <para>
/// If the event is cancelled, the mouse event will not be propagated to other views.
/// </para>
/// <para>
/// Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
/// </para>
/// <para>
/// Set <see cref="CancelEventArgs.Cancel"/> to <see langword="true"/> if the event was canceled,
/// <see langword="false"/> if not. Cancelling the event
/// prevents Views higher in the visible hierarchy from receiving Enter/Leave events.
/// </para>
/// <para>
/// See <see cref="SetHighlight"/> for more information.
/// </para>
/// </remarks>
public event EventHandler<CancelEventArgs>? MouseEnter;
#endregion MouseEnterLeave
/// <summary>Called when a mouse event occurs within the view's <see cref="Viewport"/>.</summary>
/// <remarks>
@@ -191,10 +263,7 @@ public partial class View // Mouse APIs
/// </remarks>
/// <param name="mouseEvent"></param>
/// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
protected internal virtual bool OnMouseLeave (MouseEvent mouseEvent)
{
return false;
}
protected internal virtual bool OnMouseLeave (MouseEvent mouseEvent) { return false; }
/// <summary>
/// Called when the view is to be highlighted.
@@ -316,61 +385,6 @@ public partial class View // Mouse APIs
return false;
}
/// <summary>
/// INTERNAL Called by <see cref="Application.OnMouseEvent"/> when the mouse moves over the View's <see cref="Frame"/>. <see cref="MouseLeave"/> will
/// be raised when the mouse is no longer over the <see cref="Frame"/>. If another View occludes this View, the
/// that View will also receive MouseEnter/Leave events.
/// </summary>
/// <remarks>
/// <para>
/// A view must be visible to receive Enter events (Leave events are always received).
/// </para>
/// <para>
/// This method calls <see cref="OnMouseEnter"/> to raise the <see cref="MouseEnter"/> event.
/// </para>
/// <para>
/// Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
/// </para>
/// <para>
/// See <see cref="SetHighlight"/> for more information.
/// </para>
/// </remarks>
/// <param name="mouseEvent"></param>
/// <returns><see langword="true"/> if the event was handled, <see langword="false"/> otherwise. Handling the event
/// prevents Views higher in the visible hierarchy from receiving Enter/Leave events.</returns>
internal bool? NewMouseEnterEvent (MouseEvent mouseEvent)
{
if (!Enabled)
{
return false;
}
if (!CanBeVisible (this))
{
return false;
}
if (OnMouseEnter (mouseEvent) == true)
{
return true;
}
var args = new MouseEventEventArgs (mouseEvent);
MouseEnter?.Invoke (this, args);
#if HOVER
if (HighlightStyle.HasFlag(HighlightStyle.Hover))
{
if (SetHighlight (HighlightStyle.Hover))
{
return true;
}
}
#endif
return args.Handled;
}
/// <summary>
/// INTERNAL Called by <see cref="Application.OnMouseEvent"/> when the mouse leaves <see cref="Frame"/>.
/// </summary>
@@ -435,7 +449,7 @@ public partial class View // Mouse APIs
// Enable override via virtual method and/or event
HighlightStyle copy = HighlightStyle;
var args = new CancelEventArgs<HighlightStyle> (ref copy, ref newHighlightStyle);
CancelEventArgs<HighlightStyle> args = new (ref copy, ref newHighlightStyle);
if (OnHighlight (args) == true)
{
@@ -565,7 +579,7 @@ public partial class View // Mouse APIs
}
/// <summary>
/// INTERNAL: Gets the Views that are under the mouse at <paramref name="location"/>, including Adornments.
/// INTERNAL: Gets the Views that are under the mouse at <paramref name="location"/>, including Adornments.
/// </summary>
/// <param name="location"></param>
/// <returns></returns>
@@ -636,5 +650,4 @@ public partial class View // Mouse APIs
return viewsUnderMouse;
}
}

View File

@@ -11,6 +11,8 @@
// - Raise events
// - Perhaps allow an option to not display the scrollbar arrow indicators?
using System.ComponentModel;
namespace Terminal.Gui;
/// <summary>
@@ -743,7 +745,7 @@ public class ScrollView : View
}
}
private void View_MouseEnter (object sender, MouseEventEventArgs e) { Application.GrabMouse (this); }
private void View_MouseEnter (object sender, CancelEventArgs e) { Application.GrabMouse (this); }
private void View_MouseLeave (object sender, MouseEventEventArgs e)
{

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Terminal.Gui;
@@ -245,7 +246,7 @@ public class Mouse : Scenario
Padding.MouseEnter += PaddingOnMouseEnter;
Padding.MouseLeave += PaddingOnMouseLeave;
void PaddingOnMouseEnter (object o, MouseEventEventArgs mouseEventEventArgs)
void PaddingOnMouseEnter (object o, CancelEventArgs e)
{
Padding.ColorScheme = Colors.ColorSchemes ["Error"];
}

View File

@@ -0,0 +1,576 @@
using System.ComponentModel;
namespace Terminal.Gui.ViewMouseTests;
[Trait ("Category", "Input")]
public class ApplicationMouseEnterLeaveTests
{
private class TestView : View
{
public TestView ()
{
X = 1;
Y = 1;
Width = 1;
Height = 1;
}
public bool CancelOnEnter { get; }
public int OnMouseEnterCalled { get; private set; }
public int OnMouseLeaveCalled { get; private set; }
protected override bool OnMouseEnter (CancelEventArgs eventArgs)
{
OnMouseEnterCalled++;
eventArgs.Cancel = CancelOnEnter;
base.OnMouseEnter (eventArgs);
return eventArgs.Cancel;
}
protected internal override bool OnMouseLeave (MouseEvent mouseEvent)
{
OnMouseLeaveCalled++;
base.OnMouseLeave (mouseEvent);
return mouseEvent.Handled;
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_MouseEntersView_CallsOnMouseEnter ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view = new TestView ();
Application.Top.Add (view);
var mousePosition = new Point (1, 1);
List<View> currentViewsUnderMouse = new () { view };
var mouseEvent = new MouseEvent
{
Position = mousePosition,
ScreenPosition = mousePosition
};
Application._cachedViewsUnderMouse.Clear ();
try
{
// Act
Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse, mouseEvent);
// Assert
Assert.Equal (1, view.OnMouseEnterCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_MouseLeavesView_CallsOnMouseLeave ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view = new TestView ();
Application.Top.Add (view);
var mousePosition = new Point (0, 0);
List<View> currentViewsUnderMouse = new ();
var mouseEvent = new MouseEvent ();
Application._cachedViewsUnderMouse.Clear ();
Application._cachedViewsUnderMouse.Add (view);
try
{
// Act
Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse, mouseEvent);
// Assert
Assert.Equal (0, view.OnMouseEnterCalled);
Assert.Equal (1, view.OnMouseLeaveCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMouseEnterAndLeave ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view1 = new TestView (); // at 1,1 to 2,2
var view2 = new TestView () // at 2,2 to 3,3
{
X = 2,
Y = 2
};
Application.Top.Add (view1);
Application.Top.Add (view2);
Application._cachedViewsUnderMouse.Clear ();
try
{
// Act
var mousePosition = new Point (0, 0);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (0, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (0, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new (1, 1);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (0, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new (2, 2);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new (3, 3);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (1, view2.OnMouseLeaveCalled);
// Act
mousePosition = new (0, 0);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (1, view2.OnMouseLeaveCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_NoViewsUnderMouse_DoesNotCallOnMouseEnterOrLeave ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view = new TestView ();
Application.Top.Add (view);
var mousePosition = new Point (0, 0);
List<View> currentViewsUnderMouse = new ();
var mouseEvent = new MouseEvent ();
Application._cachedViewsUnderMouse.Clear ();
try
{
// Act
Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse, mouseEvent);
// Assert
Assert.Equal (0, view.OnMouseEnterCalled);
Assert.Equal (0, view.OnMouseLeaveCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_CallsOnMouseEnterAndLeave ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view1 = new TestView
{
Width = 2
}; // at 1,1 to 3,2
var view2 = new TestView () // at 2,2 to 4,3
{
Width = 2,
X = 2,
Y = 2
};
Application.Top.Add (view1);
Application.Top.Add (view2);
Application._cachedViewsUnderMouse.Clear ();
try
{
// Act
var mousePosition = new Point (0, 0);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (0, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (0, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new (1, 1);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (0, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new (2, 2);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new (3, 3);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (1, view2.OnMouseLeaveCalled);
// Act
mousePosition = new (0, 0);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (1, view2.OnMouseLeaveCalled);
// Act
mousePosition = new (2, 2);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (2, view2.OnMouseEnterCalled);
Assert.Equal (1, view2.OnMouseLeaveCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_CallsOnMouseEnterAndLeave ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view1 = new TestView
{
Width = 2,
Height = 2,
Arrangement = ViewArrangement.Overlapped
}; // at 1,1 to 3,3 (screen)
var subView = new TestView
{
Width = 2,
Height = 2,
X = 1,
Y = 1,
Arrangement = ViewArrangement.Overlapped
}; // at 2,2 to 4,4 (screen)
view1.Add (subView);
Application.Top.Add (view1);
Application._cachedViewsUnderMouse.Clear ();
try
{
Assert.Equal (1, view1.FrameToScreen ().X);
Assert.Equal (2, subView.FrameToScreen ().X);
// Act
var mousePosition = new Point (0, 0);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (0, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (0, subView.OnMouseEnterCalled);
Assert.Equal (0, subView.OnMouseLeaveCalled);
// Act
mousePosition = new (1, 1);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (0, subView.OnMouseEnterCalled);
Assert.Equal (0, subView.OnMouseLeaveCalled);
// Act
mousePosition = new (2, 2);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (2, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (1, subView.OnMouseEnterCalled);
Assert.Equal (0, subView.OnMouseLeaveCalled);
// Act
mousePosition = new (0, 0);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (2, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, subView.OnMouseEnterCalled);
Assert.Equal (1, subView.OnMouseLeaveCalled);
// Act
mousePosition = new (2, 2);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (3, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (2, subView.OnMouseEnterCalled);
Assert.Equal (1, subView.OnMouseLeaveCalled);
// Act
mousePosition = new (3, 3);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (3, view1.OnMouseEnterCalled);
Assert.Equal (2, view1.OnMouseLeaveCalled);
Assert.Equal (2, subView.OnMouseEnterCalled);
Assert.Equal (1, subView.OnMouseLeaveCalled);
// Act
mousePosition = new (0, 0);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (3, view1.OnMouseEnterCalled);
Assert.Equal (2, view1.OnMouseLeaveCalled);
Assert.Equal (2, subView.OnMouseEnterCalled);
Assert.Equal (1, subView.OnMouseLeaveCalled);
// Act
mousePosition = new (2, 2);
Application.RaiseMouseEnterLeaveEvents (
mousePosition,
View.GetViewsUnderMouse (mousePosition),
new()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (4, view1.OnMouseEnterCalled);
Assert.Equal (2, view1.OnMouseLeaveCalled);
Assert.Equal (3, subView.OnMouseEnterCalled);
Assert.Equal (1, subView.OnMouseLeaveCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
}

View File

@@ -4,11 +4,12 @@
namespace Terminal.Gui.ApplicationTests;
public class MouseTests
[Trait ("Category", "Input")]
public class ApplicationMouseTests
{
private readonly ITestOutputHelper _output;
public MouseTests (ITestOutputHelper output)
public ApplicationMouseTests (ITestOutputHelper output)
{
_output = output;
#if DEBUG_IDISPOSABLE
@@ -401,5 +402,6 @@ public class MouseTests
Assert.Equal (0, count);
top.Dispose ();
}
#endregion
}

View File

@@ -1,337 +0,0 @@
namespace Terminal.Gui.ViewMouseTests;
[Trait ("Category", "Input")]
public class ApplicationMouseEnterLeaveTests
{
private class TestView : View
{
public TestView ()
{
X = 1;
Y = 1;
Width = 1;
Height = 1;
}
public bool HandleOnEnter { get; }
public bool HandleOnLeave { get; }
public int OnMouseEnterCalled { get; private set; }
public int OnMouseLeaveCalled { get; private set; }
protected internal override bool? OnMouseEnter (MouseEvent mouseEvent)
{
OnMouseEnterCalled++;
mouseEvent.Handled = HandleOnEnter;
base.OnMouseEnter (mouseEvent);
return mouseEvent.Handled;
}
protected internal override bool OnMouseLeave (MouseEvent mouseEvent)
{
OnMouseLeaveCalled++;
mouseEvent.Handled = HandleOnLeave;
base.OnMouseLeave (mouseEvent);
return mouseEvent.Handled;
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_MouseEntersView_CallsOnMouseEnter ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view = new TestView ();
Application.Top.Add (view);
var mousePosition = new Point (1, 1);
List<View> currentViewsUnderMouse = new () { view };
var mouseEvent = new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
};
Application._cachedViewsUnderMouse.Clear ();
try
{
// Act
Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse, mouseEvent);
// Assert
Assert.Equal (1, view.OnMouseEnterCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_MouseLeavesView_CallsOnMouseLeave ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view = new TestView ();
Application.Top.Add (view);
var mousePosition = new Point (0, 0);
List<View> currentViewsUnderMouse = new ();
var mouseEvent = new MouseEvent ();
Application._cachedViewsUnderMouse.Clear ();
Application._cachedViewsUnderMouse.Add (view);
try
{
// Act
Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse, mouseEvent);
// Assert
Assert.Equal (0, view.OnMouseEnterCalled);
Assert.Equal (1, view.OnMouseLeaveCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMouseEnterAndLeave ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view1 = new TestView (); // at 1,1 to 2,2
var view2 = new TestView () // at 2,2 to 3,3
{
X = 2,
Y = 2,
};
Application.Top.Add (view1);
Application.Top.Add (view2);
Application._cachedViewsUnderMouse.Clear ();
try
{
// Act
var mousePosition = new Point (0, 0);
Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (0, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (0, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new Point (1, 1);
Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (0, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new Point (2, 2);
Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new Point (3, 3);
Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (1, view2.OnMouseLeaveCalled);
// Act
mousePosition = new Point (0, 0);
Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (1, view2.OnMouseLeaveCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_NoViewsUnderMouse_DoesNotCallOnMouseEnterOrLeave ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view = new TestView ();
Application.Top.Add (view);
var mousePosition = new Point (0, 0);
List<View> currentViewsUnderMouse = new ();
var mouseEvent = new MouseEvent ();
Application._cachedViewsUnderMouse.Clear ();
try
{
// Act
Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse, mouseEvent);
// Assert
Assert.Equal (0, view.OnMouseEnterCalled);
Assert.Equal (0, view.OnMouseLeaveCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
[Fact]
public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingViews_CallsOnMouseEnterAndLeave ()
{
// Arrange
Application.Top = new () { Frame = new (0, 0, 10, 10) };
var view1 = new TestView ()
{
Width = 2
}; // at 1,1 to 3,2
var view2 = new TestView () // at 2,2 to 4,3
{
Width = 2,
X = 2,
Y = 2,
};
Application.Top.Add (view1);
Application.Top.Add (view2);
Application._cachedViewsUnderMouse.Clear ();
try
{
// Act
var mousePosition = new Point (0, 0);
Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (0, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (0, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new Point (1, 1);
Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (0, view1.OnMouseLeaveCalled);
Assert.Equal (0, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new Point (2, 2);
Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (0, view2.OnMouseLeaveCalled);
// Act
mousePosition = new Point (3, 3);
Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (1, view2.OnMouseLeaveCalled);
// Act
mousePosition = new Point (0, 0);
Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent ()
{
Position = mousePosition,
ScreenPosition = mousePosition
});
// Assert
Assert.Equal (1, view1.OnMouseEnterCalled);
Assert.Equal (1, view1.OnMouseLeaveCalled);
Assert.Equal (1, view2.OnMouseEnterCalled);
Assert.Equal (1, view2.OnMouseLeaveCalled);
}
finally
{
// Cleanup
Application.Top?.Dispose ();
Application.ResetState ();
}
}
}

View File

@@ -235,8 +235,6 @@ public class ResponderTests
Assert.False (r.OnKeyDown (new Key { KeyCode = KeyCode.Null }));
Assert.False (r.OnKeyUp (new Key { KeyCode = KeyCode.Null }));
Assert.False (r.NewMouseEvent (new MouseEvent { Flags = MouseFlags.AllEvents }));
Assert.False (r.NewMouseEnterEvent (new MouseEvent { Flags = MouseFlags.AllEvents }));
Assert.False (r.NewMouseLeaveEvent (new MouseEvent { Flags = MouseFlags.AllEvents }));
var v = new View ();
//Assert.False (r.OnEnter (v));

View File

@@ -2,6 +2,7 @@
namespace Terminal.Gui.ViewMouseTests;
[Trait ("Category", "Input")]
public class GetViewsUnderMouseTests
{
[Theory]
@@ -57,21 +58,30 @@ public class GetViewsUnderMouseTests
[InlineData (1, 1, 1, 1, 1, 8, 8, typeof (Padding))]
[InlineData (1, 1, 1, 1, 1, 9, 9, typeof (Border))]
[InlineData (1, 1, 1, 1, 1, 10, 10, typeof (Margin))]
public void GetViewsUnderMouse_Top_Adornments_Returns_Correct_View (int frameX, int frameY, int marginThickness, int borderThickness, int paddingThickness, int testX, int testY, Type? expectedViewType)
public void GetViewsUnderMouse_Top_Adornments_Returns_Correct_View (
int frameX,
int frameY,
int marginThickness,
int borderThickness,
int paddingThickness,
int testX,
int testY,
Type? expectedViewType
)
{
// Arrange
Application.Top = new ()
{
Frame = new Rectangle (frameX, frameY, 10, 10),
Frame = new (frameX, frameY, 10, 10)
};
Application.Top.Margin.Thickness = new Thickness (marginThickness);
Application.Top.Border.Thickness = new Thickness (borderThickness);
Application.Top.Padding.Thickness = new Thickness (paddingThickness);
Application.Top.Margin.Thickness = new (marginThickness);
Application.Top.Border.Thickness = new (borderThickness);
Application.Top.Padding.Thickness = new (paddingThickness);
var location = new Point (testX, testY);
// Act
var viewsUnderMouse = View.GetViewsUnderMouse (location);
List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
// Assert
if (expectedViewType == null)
@@ -82,8 +92,9 @@ public class GetViewsUnderMouseTests
{
Assert.Contains (viewsUnderMouse, v => v?.GetType () == expectedViewType);
}
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[Theory]
@@ -95,18 +106,18 @@ public class GetViewsUnderMouseTests
// Arrange
Application.Top = new ()
{
Frame = new Rectangle (0, 0, 10, 10)
Frame = new (0, 0, 10, 10)
};
var location = new Point (testX, testY);
// Act
var viewsUnderMouse = View.GetViewsUnderMouse (location);
List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
// Assert
Assert.Contains (viewsUnderMouse, v => v == Application.Top);
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[Theory]
@@ -118,13 +129,13 @@ public class GetViewsUnderMouseTests
// Arrange
var view = new View
{
Frame = new Rectangle (0, 0, 10, 10)
Frame = new (0, 0, 10, 10)
};
var location = new Point (testX, testY);
// Act
var viewsUnderMouse = View.GetViewsUnderMouse (location);
List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
// Assert
Assert.Empty (viewsUnderMouse);
@@ -139,14 +150,14 @@ public class GetViewsUnderMouseTests
// Arrange
var view = new View
{
Frame = new Rectangle (0, 0, 10, 10),
Frame = new (0, 0, 10, 10),
Visible = false
};
var location = new Point (testX, testY);
// Act
var viewsUnderMouse = View.GetViewsUnderMouse (location);
List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
// Assert
Assert.Empty (viewsUnderMouse);
@@ -165,12 +176,12 @@ public class GetViewsUnderMouseTests
// Arrange
Application.Top = new ()
{
Frame = new Rectangle (0, 0, 10, 10)
Frame = new (0, 0, 10, 10)
};
var subView = new View
{
Frame = new Rectangle (1, 1, 8, 8)
Frame = new (1, 1, 8, 8)
};
Application.Top.Add (subView);
@@ -178,7 +189,7 @@ public class GetViewsUnderMouseTests
var location = new Point (testX, testY);
// Act
var viewsUnderMouse = View.GetViewsUnderMouse (location);
List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
// Assert
if (expected)
@@ -189,39 +200,36 @@ public class GetViewsUnderMouseTests
{
Assert.DoesNotContain (viewsUnderMouse, v => v == subView);
}
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[Theory]
[Theory]
[InlineData (0, 0, 0, 0, 0, -1, -1, null)]
[InlineData (0, 0, 0, 0, 0, 0, 0, typeof (View))]
[InlineData (0, 0, 0, 0, 0, 1, 1, typeof (View))]
[InlineData (0, 0, 0, 0, 0, 4, 4, typeof (View))]
[InlineData (0, 0, 0, 0, 0, 9, 9, typeof (View))]
[InlineData (0, 0, 0, 0, 0, 10, 10, null)]
[InlineData (1, 1, 0, 0, 0, -1, -1, null)]
[InlineData (1, 1, 0, 0, 0, 0, 0, null)]
[InlineData (1, 1, 0, 0, 0, 1, 1, typeof (View))]
[InlineData (1, 1, 0, 0, 0, 4, 4, typeof (View))]
[InlineData (1, 1, 0, 0, 0, 9, 9, typeof (View))]
[InlineData (1, 1, 0, 0, 0, 10, 10, typeof (View))]
[InlineData (0, 0, 1, 0, 0, -1, -1, null)]
[InlineData (0, 0, 1, 0, 0, 0, 0, typeof (Margin))]
[InlineData (0, 0, 1, 0, 0, 1, 1, typeof (View))]
[InlineData (0, 0, 1, 0, 0, 4, 4, typeof (View))]
[InlineData (0, 0, 1, 0, 0, 9, 9, typeof (Margin))]
[InlineData (0, 0, 1, 0, 0, 10, 10, null)]
[InlineData (0, 0, 1, 1, 0, -1, -1, null)]
[InlineData (0, 0, 1, 1, 0, 0, 0, typeof (Margin))]
[InlineData (0, 0, 1, 1, 0, 1, 1, typeof (Border))]
[InlineData (0, 0, 1, 1, 0, 4, 4, typeof (View))]
[InlineData (0, 0, 1, 1, 0, 9, 9, typeof (Margin))]
[InlineData (0, 0, 1, 1, 0, 10, 10, null)]
[InlineData (0, 0, 1, 1, 1, -1, -1, null)]
[InlineData (0, 0, 1, 1, 1, 0, 0, typeof (Margin))]
[InlineData (0, 0, 1, 1, 1, 1, 1, typeof (Border))]
@@ -229,7 +237,6 @@ public class GetViewsUnderMouseTests
[InlineData (0, 0, 1, 1, 1, 4, 4, typeof (View))]
[InlineData (0, 0, 1, 1, 1, 9, 9, typeof (Margin))]
[InlineData (0, 0, 1, 1, 1, 10, 10, null)]
[InlineData (1, 1, 1, 0, 0, -1, -1, null)]
[InlineData (1, 1, 1, 0, 0, 0, 0, null)]
[InlineData (1, 1, 1, 0, 0, 1, 1, typeof (Margin))]
@@ -251,18 +258,28 @@ public class GetViewsUnderMouseTests
[InlineData (1, 1, 1, 1, 1, 8, 8, typeof (Padding))]
[InlineData (1, 1, 1, 1, 1, 9, 9, typeof (Border))]
[InlineData (1, 1, 1, 1, 1, 10, 10, typeof (Margin))]
public void Contains (int frameX, int frameY, int marginThickness, int borderThickness, int paddingThickness, int testX, int testY, Type? expectedAdornmentType)
public void Contains (
int frameX,
int frameY,
int marginThickness,
int borderThickness,
int paddingThickness,
int testX,
int testY,
Type? expectedAdornmentType
)
{
var view = new View ()
var view = new View
{
X = frameX, Y = frameY,
Width = 10, Height = 10,
Width = 10, Height = 10
};
view.Margin.Thickness = new Thickness (marginThickness);
view.Border.Thickness = new Thickness (borderThickness);
view.Padding.Thickness = new Thickness (paddingThickness);
view.Margin.Thickness = new (marginThickness);
view.Border.Thickness = new (borderThickness);
view.Padding.Thickness = new (paddingThickness);
Type? containedType = null;
if (view.Contains (new (testX, testY)))
{
containedType = view.GetType ();
@@ -282,8 +299,8 @@ public class GetViewsUnderMouseTests
{
containedType = view.Padding.GetType ();
}
Assert.Equal (expectedAdornmentType, containedType);
Assert.Equal (expectedAdornmentType, containedType);
}
// Test that GetViewsUnderMouse returns the correct view if the start view has no subviews
@@ -295,12 +312,12 @@ public class GetViewsUnderMouseTests
{
Application.Top = new ()
{
Width = 10, Height = 10,
Width = 10, Height = 10
};
Assert.Same (Application.Top, View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault());
Assert.Same (Application.Top, View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ());
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
// Test that GetViewsUnderMouse returns null if the start view has no subviews and coords are outside the view
@@ -313,12 +330,12 @@ public class GetViewsUnderMouseTests
Application.Top = new ()
{
X = 1, Y = 2,
Width = 10, Height = 10,
Width = 10, Height = 10
};
Assert.Null (View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault());
Assert.Null (View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ());
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[Theory]
@@ -331,12 +348,12 @@ public class GetViewsUnderMouseTests
{
X = 1, Y = 2,
Width = 10, Height = 10,
Visible = false,
Visible = false
};
Assert.Null (View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault());
Assert.Null (View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ());
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
// Test that GetViewsUnderMouse returns the correct view if the start view has subviews
@@ -346,28 +363,27 @@ public class GetViewsUnderMouseTests
[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)
{
Application.Top = new ()
{
Width = 10, Height = 10,
Width = 10, Height = 10
};
var subview = new View ()
var subview = new View
{
X = 1, Y = 2,
Width = 5, Height = 5,
Width = 5, Height = 5
};
Application.Top.Add (subview);
var found = View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedSubViewFound, found == subview);
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[Theory]
@@ -382,10 +398,10 @@ public class GetViewsUnderMouseTests
{
Application.Top = new ()
{
Width = 10, Height = 10,
Width = 10, Height = 10
};
var subview = new View ()
var subview = new View
{
X = 1, Y = 2,
Width = 5, Height = 5,
@@ -393,14 +409,13 @@ public class GetViewsUnderMouseTests
};
Application.Top.Add (subview);
var found = View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedSubViewFound, found == subview);
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[Theory]
[InlineData (0, 0, false)]
[InlineData (1, 1, false)]
@@ -417,20 +432,20 @@ public class GetViewsUnderMouseTests
Visible = false
};
var subview = new View ()
var subview = new View
{
X = 1, Y = 2,
Width = 5, Height = 5,
Width = 5, Height = 5
};
Application.Top.Add (subview);
subview.Visible = true;
Assert.True (subview.Visible);
Assert.False (Application.Top.Visible);
var found = View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedSubViewFound, found == subview);
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
// Test that GetViewsUnderMouse works if the start view has positive Adornments
@@ -441,7 +456,6 @@ public class GetViewsUnderMouseTests
[InlineData (10, 10, false)]
[InlineData (7, 8, false)]
[InlineData (1, 2, false)]
[InlineData (2, 3, true)]
[InlineData (5, 6, true)]
[InlineData (6, 7, true)]
@@ -449,22 +463,22 @@ public class GetViewsUnderMouseTests
{
Application.Top = new ()
{
Width = 10, Height = 10,
Width = 10, Height = 10
};
Application.Top.Margin.Thickness = new Thickness (1);
Application.Top.Margin.Thickness = new (1);
var subview = new View ()
var subview = new View
{
X = 1, Y = 2,
Width = 5, Height = 5,
Width = 5, Height = 5
};
Application.Top.Add (subview);
var found = View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedSubViewFound, found == subview);
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
// Test that GetViewsUnderMouse works if the start view has offset Viewport location
@@ -472,7 +486,6 @@ public class GetViewsUnderMouseTests
[InlineData (1, 0, 0, true)]
[InlineData (1, 1, 1, true)]
[InlineData (1, 2, 2, false)]
[InlineData (-1, 3, 3, true)]
[InlineData (-1, 2, 2, true)]
[InlineData (-1, 1, 1, false)]
@@ -486,18 +499,18 @@ public class GetViewsUnderMouseTests
};
Application.Top.Viewport = new (offset, offset, 10, 10);
var subview = new View ()
var subview = new View
{
X = 1, Y = 1,
Width = 2, Height = 2,
Width = 2, Height = 2
};
Application.Top.Add (subview);
var found = View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedSubViewFound, found == subview);
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[Theory]
@@ -514,59 +527,55 @@ public class GetViewsUnderMouseTests
{
Application.Top = new ()
{
Width = 10, Height = 10,
Width = 10, Height = 10
};
Application.Top.Padding.Thickness = new Thickness (1);
Application.Top.Padding.Thickness = new (1);
var subview = new View ()
var subview = new View
{
X = Pos.AnchorEnd (1), Y = Pos.AnchorEnd (1),
Width = 1, Height = 1,
Width = 1, Height = 1
};
Application.Top.Padding.Add (subview);
Application.Top.BeginInit ();
Application.Top.EndInit ();
var found = View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedSubViewFound, found == subview);
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[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 (Toplevel))]
public void Returns_Adornment_If_Start_Has_Adornments (int testX, int testY, Type expectedAdornmentType)
{
Application.Top = new ()
{
Width = 10, Height = 10,
Width = 10, Height = 10
};
Application.Top.Margin.Thickness = new Thickness (1);
Application.Top.Border.Thickness = new Thickness (1);
Application.Top.Padding.Thickness = new Thickness (1);
Application.Top.Margin.Thickness = new (1);
Application.Top.Border.Thickness = new (1);
Application.Top.Padding.Thickness = new (1);
var subview = new View ()
var subview = new View
{
X = 1, Y = 1,
Width = 1, Height = 1,
Width = 1, Height = 1
};
Application.Top.Add (subview);
var found = View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedAdornmentType, found!.GetType ());
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
// Test that GetViewsUnderMouse works if the subview has positive Adornments
@@ -579,28 +588,27 @@ public class GetViewsUnderMouseTests
[InlineData (6, 7, false)]
[InlineData (1, 2, false)]
[InlineData (5, 6, false)]
[InlineData (2, 3, true)]
public void Returns_Correct_If_SubView_Has_Adornments (int testX, int testY, bool expectedSubViewFound)
{
Application.Top = new ()
{
Width = 10, Height = 10,
Width = 10, Height = 10
};
var subview = new View ()
var subview = new View
{
X = 1, Y = 2,
Width = 5, Height = 5,
Width = 5, Height = 5
};
subview.Margin.Thickness = new Thickness (1);
subview.Margin.Thickness = new (1);
Application.Top.Add (subview);
var found = View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedSubViewFound, found == subview);
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[Theory]
@@ -618,36 +626,36 @@ public class GetViewsUnderMouseTests
{
Application.Top = new ()
{
Width = 10, Height = 10,
Width = 10, Height = 10
};
// A subview with + Padding
var subview = new View ()
var subview = new View
{
X = 1, Y = 1,
Width = 5, Height = 5,
Width = 5, Height = 5
};
subview.Padding.Thickness = new (1);
// This subview will be at the bottom-right-corner of subview
// So screen-relative location will be X + Width - 1 = 5
var paddingSubview = new View ()
var paddingSubview = new View
{
X = Pos.AnchorEnd (1),
Y = Pos.AnchorEnd (1),
Width = 1,
Height = 1,
Height = 1
};
subview.Padding.Add (paddingSubview);
Application.Top.Add (subview);
Application.Top.BeginInit ();
Application.Top.EndInit ();
var found = View.GetViewsUnderMouse(new Point(testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedSubViewFound, found == paddingSubview);
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[Theory]
@@ -665,14 +673,14 @@ public class GetViewsUnderMouseTests
{
Application.Top = new ()
{
Width = 10, Height = 10,
Width = 10, Height = 10
};
// A subview with + Padding
var subview = new View ()
var subview = new View
{
X = 1, Y = 1,
Width = 5, Height = 5,
Width = 5, Height = 5
};
subview.Padding.Thickness = new (1);
@@ -682,23 +690,23 @@ public class GetViewsUnderMouseTests
// This subview will be at the bottom-right-corner of subview
// So screen-relative location will be X + Width - 1 = 5
var paddingSubview = new View ()
var paddingSubview = new View
{
X = Pos.AnchorEnd (1),
Y = Pos.AnchorEnd (1),
Width = 1,
Height = 1,
Height = 1
};
subview.Padding.Add (paddingSubview);
Application.Top.Add (subview);
Application.Top.BeginInit ();
Application.Top.EndInit ();
var found = View.GetViewsUnderMouse(new (testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedSubViewFound, found == paddingSubview);
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
// Test that GetViewsUnderMouse works with nested subviews
@@ -706,7 +714,6 @@ public class GetViewsUnderMouseTests
[InlineData (0, 0, -1)]
[InlineData (9, 9, -1)]
[InlineData (10, 10, -1)]
[InlineData (1, 1, 0)]
[InlineData (1, 2, 0)]
[InlineData (2, 2, 1)]
@@ -719,14 +726,15 @@ public class GetViewsUnderMouseTests
Width = 10, Height = 10
};
int numSubViews = 3;
List<View> subviews = new List<View> ();
for (int i = 0; i < numSubViews; i++)
var numSubViews = 3;
List<View> subviews = new ();
for (var i = 0; i < numSubViews; i++)
{
var subview = new View ()
var subview = new View
{
X = 1, Y = 1,
Width = 5, Height = 5,
Width = 5, Height = 5
};
subviews.Add (subview);
@@ -738,9 +746,60 @@ public class GetViewsUnderMouseTests
Application.Top.Add (subviews [0]);
var found = View.GetViewsUnderMouse(new (testX, testY)).LastOrDefault();
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
Assert.Equal (expectedSubViewFound, subviews.IndexOf (found!));
Application.Top.Dispose ();
Application.ResetState (ignoreDisposed: true);
Application.ResetState (true);
}
[Theory]
[InlineData (0, 0, new [] { "top" })]
[InlineData (9, 9, new [] { "top" })]
[InlineData (10, 10, new string [] { })]
[InlineData (1, 1, new [] { "top", "view" })]
[InlineData (1, 2, new [] { "top", "view" })]
[InlineData (2, 1, new [] { "top", "view" })]
[InlineData (2, 2, new [] { "top", "view", "subView" })]
[InlineData (3, 3, new [] { "top" })] // clipped
[InlineData (2, 3, new [] { "top" })] // clipped
public void GetViewsUnderMouse_Tiled_Subviews (int mouseX, int mouseY, string [] viewIdStrings)
{
// Arrange
Application.Top = new ()
{
Frame = new (0, 0, 10, 10),
Id = "top"
};
var view = new View
{
Id = "view",
X = 1,
Y = 1,
Width = 2,
Height = 2,
Arrangement = ViewArrangement.Overlapped
}; // at 1,1 to 3,2 (screen)
var subView = new View
{
Id = "subView",
X = 1,
Y = 1,
Width = 2,
Height = 2,
Arrangement = ViewArrangement.Overlapped
}; // at 2,2 to 4,3 (screen)
view.Add (subView);
Application.Top.Add (view);
List<View?> found = View.GetViewsUnderMouse (new (mouseX, mouseY));
string [] foundIds = found.Select (v => v.Id).ToArray ();
Assert.Equal (viewIdStrings, foundIds);
Application.Top.Dispose ();
Application.ResetState (true);
}
}

View File

@@ -1,3 +1,5 @@
using System.ComponentModel;
namespace Terminal.Gui.ViewMouseTests;
[Trait ("Category", "Input")]
@@ -11,29 +13,26 @@ public class MouseEnterLeaveTests
MouseLeave += OnMouseLeaveHandler;
}
public bool HandleOnEnter { get; init; }
public bool HandleOnLeave { get; }
public bool CancelOnEnter { get; init; }
public bool HandleEnterEvent { get; init; }
public bool HandleLeaveEvent { get; }
public bool CancelEnterEvent { get; init; }
public bool OnMouseEnterCalled { get; private set; }
public bool OnMouseLeaveCalled { get; private set; }
protected internal override bool? OnMouseEnter (MouseEvent mouseEvent)
protected override bool OnMouseEnter (CancelEventArgs eventArgs)
{
OnMouseEnterCalled = true;
mouseEvent.Handled = HandleOnEnter;
eventArgs.Cancel = CancelOnEnter;
base.OnMouseEnter (mouseEvent);
base.OnMouseEnter (eventArgs);
return mouseEvent.Handled;
return eventArgs.Cancel;
}
protected internal override bool OnMouseLeave (MouseEvent mouseEvent)
{
OnMouseLeaveCalled = true;
mouseEvent.Handled = HandleOnLeave;
base.OnMouseLeave (mouseEvent);
@@ -43,25 +42,17 @@ public class MouseEnterLeaveTests
public bool MouseEnterRaised { get; private set; }
public bool MouseLeaveRaised { get; private set; }
private void OnMouseEnterHandler (object s, MouseEventEventArgs e)
private void OnMouseEnterHandler (object s, CancelEventArgs e)
{
MouseEnterRaised = true;
if (HandleEnterEvent)
if (CancelEnterEvent)
{
e.Handled = true;
e.Cancel = true;
}
}
private void OnMouseLeaveHandler (object s, MouseEventEventArgs e)
{
MouseLeaveRaised = true;
if (HandleLeaveEvent)
{
e.Handled = true;
}
}
private void OnMouseLeaveHandler (object s, MouseEventEventArgs e) { MouseLeaveRaised = true; }
}
[Fact]
@@ -76,20 +67,22 @@ public class MouseEnterLeaveTests
var mouseEvent = new MouseEvent ();
var eventArgs = new CancelEventArgs ();
// Act
bool? handled = view.NewMouseEnterEvent (mouseEvent);
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
// Assert
Assert.True (view.OnMouseEnterCalled);
Assert.False (handled);
Assert.False (mouseEvent.Handled);
Assert.False (cancelled);
Assert.False (eventArgs.Cancel);
// Cleanup
view.Dispose ();
}
[Fact]
public void NewMouseEnterEvent_ViewIsDisabled_DoesNotCallOnMouseEnter ()
public void NewMouseEnterEvent_ViewIsDisabled_CallsOnMouseEnter ()
{
// Arrange
var view = new TestView
@@ -98,15 +91,15 @@ public class MouseEnterLeaveTests
Visible = true
};
var mouseEvent = new MouseEvent ();
var eventArgs = new CancelEventArgs ();
// Act
bool? handled = view.NewMouseEnterEvent (mouseEvent);
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
// Assert
Assert.False (view.OnMouseEnterCalled);
Assert.False (handled);
Assert.False (mouseEvent.Handled);
Assert.True (view.OnMouseEnterCalled);
Assert.False (cancelled);
Assert.False (eventArgs.Cancel);
// Cleanup
view.Dispose ();
@@ -122,15 +115,15 @@ public class MouseEnterLeaveTests
Visible = false
};
var mouseEvent = new MouseEvent ();
var eventArgs = new CancelEventArgs ();
// Act
bool? handled = view.NewMouseEnterEvent (mouseEvent);
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
// Assert
Assert.False (view.OnMouseEnterCalled);
Assert.False (handled);
Assert.False (mouseEvent.Handled);
Assert.Null (cancelled);
Assert.False (eventArgs.Cancel);
// Cleanup
view.Dispose ();
@@ -148,11 +141,11 @@ public class MouseEnterLeaveTests
var mouseEvent = new MouseEvent ();
// Act
bool? handled = view.NewMouseLeaveEvent (mouseEvent);
bool? cancelled = view.NewMouseLeaveEvent (mouseEvent);
// Assert
Assert.True (view.OnMouseLeaveCalled);
Assert.False (handled);
Assert.False (cancelled);
Assert.False (mouseEvent.Handled);
// Cleanup
@@ -172,11 +165,11 @@ public class MouseEnterLeaveTests
var mouseEvent = new MouseEvent ();
// Act
bool? handled = view.NewMouseLeaveEvent (mouseEvent);
bool? cancelled = view.NewMouseLeaveEvent (mouseEvent);
// Assert
Assert.True (view.OnMouseLeaveCalled);
Assert.False (handled);
Assert.False (cancelled);
Assert.False (mouseEvent.Handled);
// Cleanup
@@ -195,22 +188,22 @@ public class MouseEnterLeaveTests
Visible = true
};
var mouseEvent = new MouseEvent ();
var eventArgs = new CancelEventArgs ();
// Act
bool? handled = view.NewMouseEnterEvent (mouseEvent);
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
// Assert
Assert.True (view.MouseEnterRaised);
Assert.False (handled);
Assert.False (mouseEvent.Handled);
Assert.False (cancelled);
Assert.False (eventArgs.Cancel);
// Cleanup
view.Dispose ();
}
[Fact]
public void NewMouseEnterEvent_ViewIsDisabled_DoesNotRaiseMouseEnter ()
public void NewMouseEnterEvent_ViewIsDisabled_RaisesMouseEnter ()
{
// Arrange
var view = new TestView
@@ -219,15 +212,15 @@ public class MouseEnterLeaveTests
Visible = true
};
var mouseEvent = new MouseEvent ();
var eventArgs = new CancelEventArgs ();
// Act
bool? handled = view.NewMouseEnterEvent (mouseEvent);
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
// Assert
Assert.False (view.MouseEnterRaised);
Assert.False (handled);
Assert.False (mouseEvent.Handled);
Assert.True (view.MouseEnterRaised);
Assert.False (cancelled);
Assert.False (eventArgs.Cancel);
// Cleanup
view.Dispose ();
@@ -243,15 +236,15 @@ public class MouseEnterLeaveTests
Visible = false
};
var mouseEvent = new MouseEvent ();
var eventArgs = new CancelEventArgs ();
// Act
bool? handled = view.NewMouseEnterEvent (mouseEvent);
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
// Assert
Assert.False (view.MouseEnterRaised);
Assert.False (handled);
Assert.False (mouseEvent.Handled);
Assert.Null (cancelled);
Assert.False (eventArgs.Cancel);
// Cleanup
view.Dispose ();
@@ -263,18 +256,18 @@ public class MouseEnterLeaveTests
// Arrange
var view = new TestView
{
Enabled = true,
Enabled = true,
Visible = true
};
var mouseEvent = new MouseEvent ();
// Act
bool? handled = view.NewMouseLeaveEvent (mouseEvent);
bool? cancelled = view.NewMouseLeaveEvent (mouseEvent);
// Assert
Assert.True (view.MouseLeaveRaised);
Assert.False (handled);
Assert.False (cancelled);
Assert.False (mouseEvent.Handled);
// Cleanup
@@ -282,7 +275,7 @@ public class MouseEnterLeaveTests
}
[Fact]
public void NewMouseLeaveEvent_ViewIsNotVisible_DoesNotRaiseMouseLeave ()
public void NewMouseLeaveEvent_ViewIsNotVisible_RaisesMouseLeave ()
{
// Arrange
var view = new TestView
@@ -294,11 +287,11 @@ public class MouseEnterLeaveTests
var mouseEvent = new MouseEvent ();
// Act
bool? handled = view.NewMouseLeaveEvent (mouseEvent);
bool? cancelled = view.NewMouseLeaveEvent (mouseEvent);
// Assert
Assert.True (view.MouseLeaveRaised);
Assert.False (handled);
Assert.False (cancelled);
Assert.False (mouseEvent.Handled);
// Cleanup
@@ -314,18 +307,18 @@ public class MouseEnterLeaveTests
{
Enabled = true,
Visible = true,
HandleOnEnter = true
CancelOnEnter = true
};
var mouseEvent = new MouseEvent ();
var eventArgs = new CancelEventArgs ();
// Act
bool? handled = view.NewMouseEnterEvent (mouseEvent);
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
// Assert
Assert.True (view.OnMouseEnterCalled);
Assert.True (handled);
Assert.True (mouseEvent.Handled);
Assert.True (cancelled);
Assert.True (eventArgs.Cancel);
Assert.False (view.MouseEnterRaised);
@@ -341,18 +334,18 @@ public class MouseEnterLeaveTests
{
Enabled = true,
Visible = true,
HandleEnterEvent = true
CancelEnterEvent = true
};
var mouseEvent = new MouseEvent ();
var eventArgs = new CancelEventArgs ();
// Act
bool? handled = view.NewMouseEnterEvent (mouseEvent);
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
// Assert
Assert.True (view.OnMouseEnterCalled);
Assert.True (handled);
Assert.True (mouseEvent.Handled);
Assert.True (cancelled);
Assert.True (eventArgs.Cancel);
Assert.True (view.MouseEnterRaised);

View File

@@ -874,8 +874,6 @@ At 0,0
//Assert.False (r.OnKeyDown (new KeyEventArgs () { Key = Key.Unknown }));
Assert.False (r.OnKeyUp (new() { KeyCode = KeyCode.Null }));
Assert.False (r.NewMouseEvent (new() { Flags = MouseFlags.AllEvents }));
Assert.False (r.NewMouseEnterEvent (new() { Flags = MouseFlags.AllEvents }));
Assert.False (r.NewMouseLeaveEvent (new() { Flags = MouseFlags.AllEvents }));
r.Dispose ();