|
|
@@ -20,7 +20,6 @@ namespace Avalonia.Input
|
|
|
private int _clickCount;
|
|
|
private Rect _lastClickRect;
|
|
|
private uint _lastClickTime;
|
|
|
- private readonly List<IInputElement> _pointerOvers = new List<IInputElement>();
|
|
|
|
|
|
/// <summary>
|
|
|
/// Intializes a new instance of <see cref="MouseDevice"/>.
|
|
|
@@ -87,6 +86,8 @@ namespace Avalonia.Input
|
|
|
/// <returns>The mouse position in the control's coordinates.</returns>
|
|
|
public Point GetPosition(IVisual relativeTo)
|
|
|
{
|
|
|
+ Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(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<ArgumentNullException>(device != null);
|
|
|
+ Contract.Requires<ArgumentNullException>(root != null);
|
|
|
+
|
|
|
ClearPointerOver(this, root);
|
|
|
}
|
|
|
|
|
|
private bool MouseDown(IMouseDevice device, uint timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers)
|
|
|
{
|
|
|
+ Contract.Requires<ArgumentNullException>(device != null);
|
|
|
+ Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(device != null);
|
|
|
+ Contract.Requires<ArgumentNullException>(root != null);
|
|
|
+
|
|
|
IInputElement source;
|
|
|
|
|
|
if (Captured == null)
|
|
|
@@ -195,8 +207,7 @@ namespace Avalonia.Input
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- var elements = Captured.GetSelfAndVisualAncestors().OfType<IInputElement>().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<ArgumentNullException>(device != null);
|
|
|
+ Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(device != null);
|
|
|
+ Contract.Requires<ArgumentNullException>(root != null);
|
|
|
+
|
|
|
var hit = HitTest(root, p);
|
|
|
|
|
|
if (hit != null)
|
|
|
@@ -260,6 +277,8 @@ namespace Avalonia.Input
|
|
|
|
|
|
private IInteractive GetSource(IVisual hit)
|
|
|
{
|
|
|
+ Contract.Requires<ArgumentNullException>(hit != null);
|
|
|
+
|
|
|
return Captured ??
|
|
|
(hit as IInteractive) ??
|
|
|
hit.GetSelfAndVisualAncestors().OfType<IInteractive>().FirstOrDefault();
|
|
|
@@ -267,22 +286,28 @@ namespace Avalonia.Input
|
|
|
|
|
|
private IInputElement HitTest(IInputElement root, Point p)
|
|
|
{
|
|
|
+ Contract.Requires<ArgumentNullException>(root != null);
|
|
|
+
|
|
|
return Captured ?? root.InputHitTest(p);
|
|
|
}
|
|
|
|
|
|
private void ClearPointerOver(IPointerDevice device, IInputRoot root)
|
|
|
{
|
|
|
- foreach (var control in _pointerOvers.ToList())
|
|
|
+ Contract.Requires<ArgumentNullException>(device != null);
|
|
|
+ Contract.Requires<ArgumentNullException>(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<ArgumentNullException>(device != null);
|
|
|
+ Contract.Requires<ArgumentNullException>(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<IInputElement> elements)
|
|
|
+ private void SetPointerOver(IPointerDevice device, IInputRoot root, IInputElement element)
|
|
|
{
|
|
|
- foreach (var control in _pointerOvers.Except(elements).ToList())
|
|
|
+ Contract.Requires<ArgumentNullException>(device != null);
|
|
|
+ Contract.Requires<ArgumentNullException>(root != null);
|
|
|
+ Contract.Requires<ArgumentNullException>(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;
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
+}
|