diff --git a/src/Avalonia.Input/IInputElement.cs b/src/Avalonia.Input/IInputElement.cs
index 786353f05c..5acb6aa777 100644
--- a/src/Avalonia.Input/IInputElement.cs
+++ b/src/Avalonia.Input/IInputElement.cs
@@ -93,17 +93,17 @@ namespace Avalonia.Input
bool IsEnabledCore { get; }
///
- /// Gets or sets a value indicating whether the control is focused.
+ /// Gets a value indicating whether the control is focused.
///
bool IsFocused { get; }
///
- /// Gets or sets a value indicating whether the control is considered for hit testing.
+ /// Gets a value indicating whether the control is considered for hit testing.
///
bool IsHitTestVisible { get; }
///
- /// Gets or sets a value indicating whether the pointer is currently over the control.
+ /// Gets a value indicating whether the pointer is currently over the control.
///
bool IsPointerOver { get; }
diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs
index 2c0cb8656c..cbcb4382ec 100644
--- a/src/Avalonia.Input/MouseDevice.cs
+++ b/src/Avalonia.Input/MouseDevice.cs
@@ -20,7 +20,6 @@ namespace Avalonia.Input
private int _clickCount;
private Rect _lastClickRect;
private uint _lastClickTime;
- private readonly List _pointerOvers = new List();
///
/// Intializes a new instance of .
@@ -87,6 +86,8 @@ namespace Avalonia.Input
/// The mouse position in the control's coordinates.
public Point GetPosition(IVisual relativeTo)
{
+ Contract.Requires(relativeTo != null);
+
Point p = default(Point);
IVisual v = relativeTo;
IVisual root = null;
@@ -103,6 +104,8 @@ namespace Avalonia.Input
private void ProcessRawEvent(RawMouseEventArgs e)
{
+ Contract.Requires(e != null);
+
var mouse = (IMouseDevice)e.Device;
Position = e.Root.PointToScreen(e.Position);
@@ -141,11 +144,17 @@ namespace Avalonia.Input
private void LeaveWindow(IMouseDevice device, IInputRoot root)
{
+ Contract.Requires(device != null);
+ Contract.Requires(root != null);
+
ClearPointerOver(this, root);
}
private bool MouseDown(IMouseDevice device, uint timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers)
{
+ Contract.Requires(device != null);
+ Contract.Requires(root != null);
+
var hit = HitTest(root, p);
if (hit != null)
@@ -187,6 +196,9 @@ namespace Avalonia.Input
private bool MouseMove(IMouseDevice device, IInputRoot root, Point p, InputModifiers inputModifiers)
{
+ Contract.Requires(device != null);
+ Contract.Requires(root != null);
+
IInputElement source;
if (Captured == null)
@@ -195,8 +207,7 @@ namespace Avalonia.Input
}
else
{
- var elements = Captured.GetSelfAndVisualAncestors().OfType().ToList();
- SetPointerOver(this, root, elements);
+ SetPointerOver(this, root, Captured);
source = Captured;
}
@@ -208,12 +219,15 @@ namespace Avalonia.Input
InputModifiers = inputModifiers
};
- source.RaiseEvent(e);
+ source?.RaiseEvent(e);
return e.Handled;
}
private bool MouseUp(IMouseDevice device, IInputRoot root, Point p, MouseButton button, InputModifiers inputModifiers)
{
+ Contract.Requires(device != null);
+ Contract.Requires(root != null);
+
var hit = HitTest(root, p);
if (hit != null)
@@ -237,6 +251,9 @@ namespace Avalonia.Input
private bool MouseWheel(IMouseDevice device, IInputRoot root, Point p, Vector delta, InputModifiers inputModifiers)
{
+ Contract.Requires(device != null);
+ Contract.Requires(root != null);
+
var hit = HitTest(root, p);
if (hit != null)
@@ -260,6 +277,8 @@ namespace Avalonia.Input
private IInteractive GetSource(IVisual hit)
{
+ Contract.Requires(hit != null);
+
return Captured ??
(hit as IInteractive) ??
hit.GetSelfAndVisualAncestors().OfType().FirstOrDefault();
@@ -267,22 +286,28 @@ namespace Avalonia.Input
private IInputElement HitTest(IInputElement root, Point p)
{
+ Contract.Requires(root != null);
+
return Captured ?? root.InputHitTest(p);
}
private void ClearPointerOver(IPointerDevice device, IInputRoot root)
{
- foreach (var control in _pointerOvers.ToList())
+ Contract.Requires(device != null);
+ Contract.Requires(root != null);
+
+ var element = root.PointerOverElement;
+ var e = new PointerEventArgs
{
- PointerEventArgs e = new PointerEventArgs
- {
- RoutedEvent = InputElement.PointerLeaveEvent,
- Device = device,
- Source = control,
- };
+ RoutedEvent = InputElement.PointerLeaveEvent,
+ Device = device,
+ };
- _pointerOvers.Remove(control);
- control.RaiseEvent(e);
+ while (element != null)
+ {
+ e.Source = element;
+ element.RaiseEvent(e);
+ element = (IInputElement)element.VisualParent;
}
root.PointerOverElement = null;
@@ -290,40 +315,66 @@ namespace Avalonia.Input
private IInputElement SetPointerOver(IPointerDevice device, IInputRoot root, Point p)
{
- var elements = root.GetInputElementsAt(p).ToList();
- return SetPointerOver(device, root, elements);
+ Contract.Requires(device != null);
+ Contract.Requires(root != null);
+
+ var element = root.InputHitTest(p);
+
+ if (element != root.PointerOverElement)
+ {
+ if (element != null)
+ {
+ SetPointerOver(device, root, element);
+ }
+ else
+ {
+ ClearPointerOver(device, root);
+ }
+ }
+
+ return element;
}
- private IInputElement SetPointerOver(IPointerDevice device, IInputRoot root, IList elements)
+ private void SetPointerOver(IPointerDevice device, IInputRoot root, IInputElement element)
{
- foreach (var control in _pointerOvers.Except(elements).ToList())
+ Contract.Requires(device != null);
+ Contract.Requires(root != null);
+ Contract.Requires(element != null);
+
+ IInputElement branch = null;
+
+ var e = new PointerEventArgs
{
- PointerEventArgs e = new PointerEventArgs
- {
- RoutedEvent = InputElement.PointerLeaveEvent,
- Device = device,
- Source = control,
- };
+ RoutedEvent = InputElement.PointerEnterEvent,
+ Device = device,
+ };
- _pointerOvers.Remove(control);
- control.RaiseEvent(e);
- }
+ var el = element;
- foreach (var control in elements.Except(_pointerOvers))
+ while (el != null)
{
- PointerEventArgs e = new PointerEventArgs
+ if (el.IsPointerOver)
{
- RoutedEvent = InputElement.PointerEnterEvent,
- Device = device,
- Source = control,
- };
+ branch = el;
+ break;
+ }
- _pointerOvers.Add(control);
- control.RaiseEvent(e);
+ e.Source = el;
+ el.RaiseEvent(e);
+ el = (IInputElement)el.VisualParent;
+ }
+
+ el = root.PointerOverElement;
+ e.RoutedEvent = InputElement.PointerLeaveEvent;
+
+ while (el != null && el != branch)
+ {
+ e.Source = el;
+ el.RaiseEvent(e);
+ el = (IInputElement)el.VisualParent;
}
- root.PointerOverElement = elements.FirstOrDefault() ?? root;
- return root.PointerOverElement;
+ root.PointerOverElement = element;
}
}
-}
+}
\ No newline at end of file