Browse Source

Make sure IsPointerOver is correctly set.

Reset the handled state for `PointerEnter`/`PointerLeave` on each control invocation, and don't allow the user to skip setting `IsPointerOver`
pull/1811/head
Steven Kirk 8 years ago
parent
commit
3f0c7650e5
  1. 26
      src/Avalonia.Input/InputElement.cs
  2. 4
      src/Avalonia.Input/MouseDevice.cs
  3. 92
      tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs

26
src/Avalonia.Input/InputElement.cs

@ -162,8 +162,8 @@ namespace Avalonia.Input
KeyDownEvent.AddClassHandler<InputElement>(x => x.OnKeyDown);
KeyUpEvent.AddClassHandler<InputElement>(x => x.OnKeyUp);
TextInputEvent.AddClassHandler<InputElement>(x => x.OnTextInput);
PointerEnterEvent.AddClassHandler<InputElement>(x => x.OnPointerEnter);
PointerLeaveEvent.AddClassHandler<InputElement>(x => x.OnPointerLeave);
PointerEnterEvent.AddClassHandler<InputElement>(x => x.OnPointerEnterCore);
PointerLeaveEvent.AddClassHandler<InputElement>(x => x.OnPointerLeaveCore);
PointerMovedEvent.AddClassHandler<InputElement>(x => x.OnPointerMoved);
PointerPressedEvent.AddClassHandler<InputElement>(x => x.OnPointerPressed);
PointerReleasedEvent.AddClassHandler<InputElement>(x => x.OnPointerReleased);
@ -445,7 +445,6 @@ namespace Avalonia.Input
/// <param name="e">The event args.</param>
protected virtual void OnPointerEnter(PointerEventArgs e)
{
IsPointerOver = true;
}
/// <summary>
@ -454,7 +453,6 @@ namespace Avalonia.Input
/// <param name="e">The event args.</param>
protected virtual void OnPointerLeave(PointerEventArgs e)
{
IsPointerOver = false;
}
/// <summary>
@ -494,6 +492,26 @@ namespace Avalonia.Input
((InputElement)e.Sender).UpdateIsEnabledCore();
}
/// <summary>
/// Called before the <see cref="PointerEnter"/> event occurs.
/// </summary>
/// <param name="e">The event args.</param>
private void OnPointerEnterCore(PointerEventArgs e)
{
IsPointerOver = true;
OnPointerEnter(e);
}
/// <summary>
/// Called before the <see cref="PointerLeave"/> event occurs.
/// </summary>
/// <param name="e">The event args.</param>
private void OnPointerLeaveCore(PointerEventArgs e)
{
IsPointerOver = false;
OnPointerLeave(e);
}
/// <summary>
/// Updates the <see cref="IsEnabledCore"/> property value.
/// </summary>

4
src/Avalonia.Input/MouseDevice.cs

@ -363,6 +363,7 @@ namespace Avalonia.Input
}
e.Source = el;
e.Handled = false;
el.RaiseEvent(e);
el = (IInputElement)el.VisualParent;
}
@ -373,6 +374,7 @@ namespace Avalonia.Input
while (el != null && el != branch)
{
e.Source = el;
e.Handled = false;
el.RaiseEvent(e);
el = (IInputElement)el.VisualParent;
}
@ -380,4 +382,4 @@ namespace Avalonia.Input
root.PointerOverElement = element;
}
}
}
}

92
tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs

@ -30,7 +30,7 @@ namespace Avalonia.Input.UnitTests
}
[Fact]
public void MouseMove_Should_Update_PointerOver()
public void MouseMove_Should_Update_IsPointerOver()
{
var renderer = new Mock<IRenderer>();
@ -59,40 +59,92 @@ namespace Avalonia.Input.UnitTests
}
};
renderer.Setup(x => x.HitTest(It.IsAny<Point>(), It.IsAny<IVisual>(), It.IsAny<Func<IVisual, bool>>()))
.Returns(new[] { decorator });
inputManager.ProcessInput(new RawMouseEventArgs(
root.MouseDevice,
0,
root,
RawMouseEventType.Move,
new Point(),
InputModifiers.None));
SetHit(renderer, decorator);
SendMouseMove(inputManager, root);
Assert.True(decorator.IsPointerOver);
Assert.True(border.IsPointerOver);
Assert.False(canvas.IsPointerOver);
Assert.True(root.IsPointerOver);
renderer.Setup(x => x.HitTest(It.IsAny<Point>(), It.IsAny<IVisual>(), It.IsAny<Func<IVisual, bool>>()))
.Returns(new[] { canvas });
SetHit(renderer, canvas);
SendMouseMove(inputManager, root);
Assert.False(decorator.IsPointerOver);
Assert.False(border.IsPointerOver);
Assert.True(canvas.IsPointerOver);
Assert.True(root.IsPointerOver);
}
}
[Fact]
public void IsPointerOver_Should_Be_Updated_When_Child_Sets_Handled_True()
{
var renderer = new Mock<IRenderer>();
using (TestApplication(renderer.Object))
{
var inputManager = InputManager.Instance;
Canvas canvas;
Border border;
Decorator decorator;
var root = new TestRoot
{
MouseDevice = new MouseDevice(),
Renderer = renderer.Object,
Child = new Panel
{
Children =
{
(canvas = new Canvas()),
(border = new Border
{
Child = decorator = new Decorator(),
})
}
}
};
inputManager.ProcessInput(new RawMouseEventArgs(
root.MouseDevice,
0,
root,
RawMouseEventType.Move,
new Point(),
InputModifiers.None));
SetHit(renderer, canvas);
SendMouseMove(inputManager, root);
Assert.False(decorator.IsPointerOver);
Assert.False(border.IsPointerOver);
Assert.True(canvas.IsPointerOver);
Assert.True(root.IsPointerOver);
// Ensure that e.Handled is reset between controls.
decorator.PointerEnter += (s, e) => e.Handled = true;
SetHit(renderer, decorator);
SendMouseMove(inputManager, root);
Assert.True(decorator.IsPointerOver);
Assert.True(border.IsPointerOver);
Assert.False(canvas.IsPointerOver);
Assert.True(root.IsPointerOver);
}
}
private void SendMouseMove(IInputManager inputManager, TestRoot root)
{
inputManager.ProcessInput(new RawMouseEventArgs(
root.MouseDevice,
0,
root,
RawMouseEventType.Move,
new Point(),
InputModifiers.None));
}
private void SetHit(Mock<IRenderer> renderer, IControl hit)
{
renderer.Setup(x => x.HitTest(It.IsAny<Point>(), It.IsAny<IVisual>(), It.IsAny<Func<IVisual, bool>>()))
.Returns(new[] { hit });
}
private IDisposable TestApplication(IRenderer renderer)
{
return UnitTestApplication.Start(

Loading…
Cancel
Save