Forráskód Böngészése

Merge pull request #937 from AvaloniaUI/fix-mouseover

Ported changes to Avalonia.Input from scenegraph.
Steven Kirk 8 éve
szülő
commit
42400f52be
2 módosított fájl, 91 hozzáadás és 40 törlés
  1. 3 3
      src/Avalonia.Input/IInputElement.cs
  2. 88 37
      src/Avalonia.Input/MouseDevice.cs

+ 3 - 3
src/Avalonia.Input/IInputElement.cs

@@ -93,17 +93,17 @@ namespace Avalonia.Input
         bool IsEnabledCore { get; }
 
         /// <summary>
-        /// Gets or sets a value indicating whether the control is focused.
+        /// Gets a value indicating whether the control is focused.
         /// </summary>
         bool IsFocused { get; }
 
         /// <summary>
-        /// 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.
         /// </summary>
         bool IsHitTestVisible { get; }
 
         /// <summary>
-        /// 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.
         /// </summary>
         bool IsPointerOver { get; }
 

+ 88 - 37
src/Avalonia.Input/MouseDevice.cs

@@ -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;
         }
     }
-}
+}