diff --git a/Perspex.Input/IInputManager.cs b/Perspex.Input/IInputManager.cs index 0cddac4e33..1abfa053f8 100644 --- a/Perspex.Input/IInputManager.cs +++ b/Perspex.Input/IInputManager.cs @@ -14,6 +14,8 @@ namespace Perspex.Input { IObservable RawEventReceived { get; } + void ClearPointerOver(IPointerDevice device); + void Process(RawInputEventArgs e); void SetPointerOver(IPointerDevice device, IVisual visual, Point p); diff --git a/Perspex.Input/InputManager.cs b/Perspex.Input/InputManager.cs index dc86cf8016..16086d421c 100644 --- a/Perspex.Input/InputManager.cs +++ b/Perspex.Input/InputManager.cs @@ -23,6 +23,23 @@ namespace Perspex.Input get { return this.rawEventReceived; } } + public void ClearPointerOver(IPointerDevice device) + { + foreach (var control in this.pointerOvers.ToList()) + { + PointerEventArgs e = new PointerEventArgs + { + RoutedEvent = InputElement.PointerLeaveEvent, + Device = device, + OriginalSource = control, + Source = control, + }; + + this.pointerOvers.Remove(control); + control.RaiseEvent(e); + } + } + public void Process(RawInputEventArgs e) { this.rawEventReceived.OnNext(e); diff --git a/Perspex.Input/MouseDevice.cs b/Perspex.Input/MouseDevice.cs index bf3c56e9d9..b0808ca8e4 100644 --- a/Perspex.Input/MouseDevice.cs +++ b/Perspex.Input/MouseDevice.cs @@ -76,8 +76,8 @@ namespace Perspex.Input switch (e.Type) { - case RawMouseEventType.Move: - this.MouseMove(mouse, e.Root, e.Position); + case RawMouseEventType.LeaveWindow: + this.LeaveWindow(mouse, e.Root); break; case RawMouseEventType.LeftButtonDown: this.MouseDown(mouse, e.Timestamp, e.Root, e.Position); @@ -85,44 +85,18 @@ namespace Perspex.Input case RawMouseEventType.LeftButtonUp: this.MouseUp(mouse, e.Root, e.Position); break; + case RawMouseEventType.Move: + this.MouseMove(mouse, e.Root, e.Position); + break; case RawMouseEventType.Wheel: this.MouseWheel(mouse, e.Root, e.Position, ((RawMouseWheelEventArgs)e).Delta); break; } } - private void MouseMove(IMouseDevice device, IInputElement root, Point p) + private void LeaveWindow(IMouseDevice device, IInputElement root) { - IInteractive source; - - if (this.Captured == null) - { - this.InputManager.SetPointerOver(this, root, p); - source = root as IInteractive; - } - else - { - Point offset = new Point(); - - foreach (IVisual ancestor in this.Captured.GetVisualAncestors()) - { - offset += ancestor.Bounds.Position; - } - - this.InputManager.SetPointerOver(this, this.Captured, p - offset); - source = this.Captured as IInteractive; - } - - if (source != null) - { - source.RaiseEvent(new PointerEventArgs - { - Device = this, - RoutedEvent = InputElement.PointerMovedEvent, - OriginalSource = source, - Source = source, - }); - } + this.InputManager.ClearPointerOver(this); } private void MouseDown(IMouseDevice device, uint timestamp, IInputElement root, Point p) @@ -167,6 +141,40 @@ namespace Perspex.Input } } + private void MouseMove(IMouseDevice device, IInputElement root, Point p) + { + IInteractive source; + + if (this.Captured == null) + { + this.InputManager.SetPointerOver(this, root, p); + source = root as IInteractive; + } + else + { + Point offset = new Point(); + + foreach (IVisual ancestor in this.Captured.GetVisualAncestors()) + { + offset += ancestor.Bounds.Position; + } + + this.InputManager.SetPointerOver(this, this.Captured, p - offset); + source = this.Captured as IInteractive; + } + + if (source != null) + { + source.RaiseEvent(new PointerEventArgs + { + Device = this, + RoutedEvent = InputElement.PointerMovedEvent, + OriginalSource = source, + Source = source, + }); + } + } + private void MouseUp(IMouseDevice device, IInputElement root, Point p) { var hit = this.HitTest(root, p); diff --git a/Perspex.Input/Raw/RawMouseEventArgs.cs b/Perspex.Input/Raw/RawMouseEventArgs.cs index 5c08ea3800..2fe476bb51 100644 --- a/Perspex.Input/Raw/RawMouseEventArgs.cs +++ b/Perspex.Input/Raw/RawMouseEventArgs.cs @@ -7,13 +7,13 @@ namespace Perspex.Input.Raw { using System; - using Perspex.Layout; public enum RawMouseEventType { - Move, + LeaveWindow, LeftButtonDown, LeftButtonUp, + Move, Wheel, } diff --git a/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs b/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs index cba2a32f43..6a97802cfb 100644 --- a/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs +++ b/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs @@ -548,6 +548,9 @@ namespace Perspex.Win32.Interop int bufferSize, uint flags); + [DllImport("user32.dll", SetLastError = true)] + public static extern bool TrackMouseEvent(ref TRACKMOUSEEVENT lpEventTrack); + [DllImport("user32.dll")] public static extern bool TranslateMessage(ref MSG lpMsg); @@ -608,6 +611,14 @@ namespace Perspex.Win32.Interop public int bottom; } + public struct TRACKMOUSEEVENT + { + public int cbSize; + public uint dwFlags; + public IntPtr hwndTrack; + public int dwHoverTime; + } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct WNDCLASSEX { diff --git a/Windows/Perspex.Win32/WindowImpl.cs b/Windows/Perspex.Win32/WindowImpl.cs index 97f12c3b27..f20eec12d9 100644 --- a/Windows/Perspex.Win32/WindowImpl.cs +++ b/Windows/Perspex.Win32/WindowImpl.cs @@ -26,6 +26,8 @@ namespace Perspex.Win32 private Window owner; + private bool trackingMouse; + public WindowImpl() { this.CreateWindow(); @@ -188,6 +190,19 @@ namespace Perspex.Win32 break; case UnmanagedMethods.WindowsMessage.WM_MOUSEMOVE: + if (!this.trackingMouse) + { + var tm = new UnmanagedMethods.TRACKMOUSEEVENT + { + cbSize = Marshal.SizeOf(typeof(UnmanagedMethods.TRACKMOUSEEVENT)), + dwFlags = 2, + hwndTrack = this.hwnd, + dwHoverTime = 0, + }; + + UnmanagedMethods.TrackMouseEvent(ref tm); + } + e = new RawMouseEventArgs( WindowsMouseDevice.Instance, timestamp, @@ -205,6 +220,16 @@ namespace Perspex.Win32 new Vector(0, ((int)wParam >> 16) / WheelDelta)); break; + case UnmanagedMethods.WindowsMessage.WM_MOUSELEAVE: + this.trackingMouse = false; + e = new RawMouseEventArgs( + WindowsMouseDevice.Instance, + timestamp, + this.owner, + RawMouseEventType.LeaveWindow, + new Point()); + break; + case UnmanagedMethods.WindowsMessage.WM_PAINT: UnmanagedMethods.PAINTSTRUCT ps;