From 85a5288fdd835481764ffc6c10dfb77c90708c3a Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 6 Jun 2019 12:47:05 +0300 Subject: [PATCH] Propagate mouse pointer capture to Win32 API --- src/Avalonia.Input/MouseDevice.cs | 9 ++++-- src/Avalonia.Input/Pointer.cs | 6 ++++ .../Wpf/WpfMouseDevice.cs | 28 +++++++++++++------ .../Input/WindowsMouseDevice.cs | 26 ++++++++++++----- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 ++ 5 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index 4c4d679087..ee7d0c9501 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -20,8 +20,13 @@ namespace Avalonia.Input private Rect _lastClickRect; private ulong _lastClickTime; - private readonly Pointer _pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); + private readonly Pointer _pointer; + public MouseDevice(Pointer pointer = null) + { + _pointer = pointer ?? new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); + } + /// /// Gets the control that is currently capturing by the mouse, if any. /// @@ -51,7 +56,7 @@ namespace Avalonia.Input /// within the control's bounds or not. The current mouse capture control is exposed /// by the property. /// - public virtual void Capture(IInputElement control) + public void Capture(IInputElement control) { _pointer.Capture(control); } diff --git a/src/Avalonia.Input/Pointer.cs b/src/Avalonia.Input/Pointer.cs index 890ad57024..80d803abb1 100644 --- a/src/Avalonia.Input/Pointer.cs +++ b/src/Avalonia.Input/Pointer.cs @@ -27,6 +27,11 @@ namespace Avalonia.Input var seen = new HashSet(control1.GetSelfAndVisualAncestors().OfType()); return control2.GetSelfAndVisualAncestors().OfType().FirstOrDefault(seen.Contains); } + + protected virtual void PlatformCapture(IInputElement element) + { + + } public void Capture(IInputElement control) { @@ -34,6 +39,7 @@ namespace Avalonia.Input Captured.DetachedFromVisualTree -= OnCaptureDetached; var oldCapture = control; Captured = control; + PlatformCapture(control); if (oldCapture != null) { var commonParent = FindCommonParent(control, oldCapture); diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs index 0d93115714..ebfe8cde47 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfMouseDevice.cs @@ -9,22 +9,32 @@ namespace Avalonia.Win32.Interop.Wpf { private readonly WpfTopLevelImpl _impl; - public WpfMouseDevice(WpfTopLevelImpl impl) + public WpfMouseDevice(WpfTopLevelImpl impl) : base(new WpfMousePointer(impl)) { _impl = impl; } - public override void Capture(IInputElement control) + class WpfMousePointer : Pointer { - if (control == null) + private readonly WpfTopLevelImpl _impl; + + public WpfMousePointer(WpfTopLevelImpl impl) : base(Pointer.GetNextFreeId(), PointerType.Mouse, true) + { + _impl = impl; + } + + protected override void PlatformCapture(IInputElement control) { - System.Windows.Input.Mouse.Capture(null); + if (control == null) + { + System.Windows.Input.Mouse.Capture(null); + } + else if ((control.GetVisualRoot() as EmbeddableControlRoot)?.PlatformImpl != _impl) + throw new ArgumentException("Visual belongs to unknown toplevel"); + else + System.Windows.Input.Mouse.Capture(_impl); } - else if ((control.GetVisualRoot() as EmbeddableControlRoot)?.PlatformImpl != _impl) - throw new ArgumentException("Visual belongs to unknown toplevel"); - else - System.Windows.Input.Mouse.Capture(_impl); - base.Capture(control); } + } } diff --git a/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs b/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs index 2b4105efee..e7c379ad89 100644 --- a/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs +++ b/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs @@ -1,7 +1,10 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; +using Avalonia.Controls; using Avalonia.Input; +using Avalonia.VisualTree; using Avalonia.Win32.Interop; namespace Avalonia.Win32.Input @@ -10,23 +13,32 @@ namespace Avalonia.Win32.Input { public static WindowsMouseDevice Instance { get; } = new WindowsMouseDevice(); + public WindowsMouseDevice() : base(new WindowsMousePointer()) + { + + } + public WindowImpl CurrentWindow { get; set; } - public override void Capture(IInputElement control) + class WindowsMousePointer : Pointer { - base.Capture(control); - - if (control != null) + public WindowsMousePointer() : base(Pointer.GetNextFreeId(),PointerType.Mouse, true) { - UnmanagedMethods.SetCapture(CurrentWindow.Handle.Handle); } - else + + protected override void PlatformCapture(IInputElement element) { - UnmanagedMethods.ReleaseCapture(); + var hwnd = ((element?.GetVisualRoot() as TopLevel)?.PlatformImpl as WindowImpl) + ?.Handle.Handle; + + if (hwnd.HasValue && hwnd != IntPtr.Zero) + UnmanagedMethods.SetCapture(hwnd.Value); + else + UnmanagedMethods.ReleaseCapture(); } } } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 9e8b2d58a6..5cc148fa0d 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -336,6 +336,7 @@ namespace Avalonia.Win32 public void BeginMoveDrag() { + WindowsMouseDevice.Instance.Capture(null); UnmanagedMethods.DefWindowProc(_hwnd, (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN, new IntPtr((int)UnmanagedMethods.HitTestValues.HTCAPTION), IntPtr.Zero); } @@ -357,6 +358,7 @@ namespace Avalonia.Win32 #if USE_MANAGED_DRAG _managedDrag.BeginResizeDrag(edge, ScreenToClient(MouseDevice.Position)); #else + WindowsMouseDevice.Instance.Capture(null); UnmanagedMethods.DefWindowProc(_hwnd, (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN, new IntPtr((int)EdgeDic[edge]), IntPtr.Zero); #endif