diff --git a/src/Avalonia.Base/Input/Cursor.cs b/src/Avalonia.Base/Input/Cursor.cs index 122838f682..98c4258a90 100644 --- a/src/Avalonia.Base/Input/Cursor.cs +++ b/src/Avalonia.Base/Input/Cursor.cs @@ -32,10 +32,7 @@ namespace Avalonia.Input DragCopy, DragLink, None, - - [Obsolete("Use BottomSide")] - BottomSize = BottomSide - + // Not available in GTK directly, see http://www.pixelbeat.org/programming/x_cursors/ // We might enable them later, preferably, by loading pixmax directly from theme with fallback image // SizeNorthWestSouthEast, diff --git a/src/Avalonia.Base/Input/DragEventArgs.cs b/src/Avalonia.Base/Input/DragEventArgs.cs index 22ca8358ff..0e613c0f21 100644 --- a/src/Avalonia.Base/Input/DragEventArgs.cs +++ b/src/Avalonia.Base/Input/DragEventArgs.cs @@ -13,9 +13,6 @@ namespace Avalonia.Input public IDataObject Data { get; private set; } - [Obsolete("Use KeyModifiers")] - public InputModifiers Modifiers { get; private set; } - public KeyModifiers KeyModifiers { get; private set; } public Point GetPosition(IVisual relativeTo) @@ -35,17 +32,6 @@ namespace Avalonia.Input return point; } - [Obsolete("Use constructor taking KeyModifiers")] - public DragEventArgs(RoutedEvent routedEvent, IDataObject data, Interactive target, Point targetLocation, InputModifiers modifiers) - : base(routedEvent) - { - Data = data; - _target = target; - _targetLocation = targetLocation; - Modifiers = modifiers; - KeyModifiers = (KeyModifiers)(((int)modifiers) & 0xF); - } - public DragEventArgs(RoutedEvent routedEvent, IDataObject data, Interactive target, Point targetLocation, KeyModifiers keyModifiers) : base(routedEvent) { @@ -53,10 +39,6 @@ namespace Avalonia.Input _target = target; _targetLocation = targetLocation; KeyModifiers = keyModifiers; -#pragma warning disable CS0618 // Type or member is obsolete - Modifiers = (InputModifiers)keyModifiers; -#pragma warning restore CS0618 // Type or member is obsolete } - } } diff --git a/src/Avalonia.Base/Input/GotFocusEventArgs.cs b/src/Avalonia.Base/Input/GotFocusEventArgs.cs index 9d958823fe..5cce138ee0 100644 --- a/src/Avalonia.Base/Input/GotFocusEventArgs.cs +++ b/src/Avalonia.Base/Input/GotFocusEventArgs.cs @@ -1,4 +1,3 @@ -using System; using Avalonia.Interactivity; namespace Avalonia.Input @@ -13,16 +12,6 @@ namespace Avalonia.Input /// public NavigationMethod NavigationMethod { get; set; } - /// - /// Gets or sets any input modifiers active at the time of focus. - /// - [Obsolete("Use KeyModifiers")] - public InputModifiers InputModifiers - { - get => (InputModifiers)KeyModifiers; - set => KeyModifiers = (KeyModifiers)((int)value & 0xF); - } - /// /// Gets or sets any key modifiers active at the time of focus. /// diff --git a/src/Avalonia.Base/Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs index 7edc69df52..344a4eefd7 100644 --- a/src/Avalonia.Base/Input/IInputRoot.cs +++ b/src/Avalonia.Base/Input/IInputRoot.cs @@ -27,10 +27,5 @@ namespace Avalonia.Input /// Gets or sets a value indicating whether access keys are shown in the window. /// bool ShowAccessKeys { get; set; } - - /// - /// Gets associated mouse device - /// - IMouseDevice? MouseDevice { get; } } } diff --git a/src/Avalonia.Base/Input/IKeyboardDevice.cs b/src/Avalonia.Base/Input/IKeyboardDevice.cs index 80aebc02bc..0b7b5aaecc 100644 --- a/src/Avalonia.Base/Input/IKeyboardDevice.cs +++ b/src/Avalonia.Base/Input/IKeyboardDevice.cs @@ -4,19 +4,6 @@ using Avalonia.Metadata; namespace Avalonia.Input { - [Flags, Obsolete("Use KeyModifiers and PointerPointProperties")] - public enum InputModifiers - { - None = 0, - Alt = 1, - Control = 2, - Shift = 4, - Windows = 8, - LeftMouseButton = 16, - RightMouseButton = 32, - MiddleMouseButton = 64 - } - [Flags] public enum KeyModifiers { diff --git a/src/Avalonia.Base/Input/IMouseDevice.cs b/src/Avalonia.Base/Input/IMouseDevice.cs index 2d66397d63..00c436bf21 100644 --- a/src/Avalonia.Base/Input/IMouseDevice.cs +++ b/src/Avalonia.Base/Input/IMouseDevice.cs @@ -1,4 +1,3 @@ -using System; using Avalonia.Metadata; namespace Avalonia.Input @@ -9,16 +8,5 @@ namespace Avalonia.Input [NotClientImplementable] public interface IMouseDevice : IPointerDevice { - /// - /// Gets the mouse position, in screen coordinates. - /// - [Obsolete("Use PointerEventArgs.GetPosition")] - PixelPoint Position { get; } - - [Obsolete] - void TopLevelClosed(IInputRoot root); - - [Obsolete] - void SceneInvalidated(IInputRoot root, Rect rect); } } diff --git a/src/Avalonia.Base/Input/IPointerDevice.cs b/src/Avalonia.Base/Input/IPointerDevice.cs index 0993835feb..e0aebda9c5 100644 --- a/src/Avalonia.Base/Input/IPointerDevice.cs +++ b/src/Avalonia.Base/Input/IPointerDevice.cs @@ -1,5 +1,3 @@ -using System; -using Avalonia.VisualTree; using Avalonia.Input.Raw; using Avalonia.Metadata; @@ -8,18 +6,6 @@ namespace Avalonia.Input [NotClientImplementable] public interface IPointerDevice : IInputDevice { - /// - [Obsolete("Use IPointer")] - IInputElement? Captured { get; } - - /// - [Obsolete("Use IPointer")] - void Capture(IInputElement? control); - - /// - [Obsolete("Use PointerEventArgs.GetPosition")] - Point GetPosition(IVisual relativeTo); - /// /// Gets a pointer for specific event args. /// diff --git a/src/Avalonia.Base/Input/KeyEventArgs.cs b/src/Avalonia.Base/Input/KeyEventArgs.cs index 67cd5a520a..b8291e9096 100644 --- a/src/Avalonia.Base/Input/KeyEventArgs.cs +++ b/src/Avalonia.Base/Input/KeyEventArgs.cs @@ -9,8 +9,6 @@ namespace Avalonia.Input public Key Key { get; set; } - [Obsolete("Use KeyModifiers")] - public InputModifiers Modifiers => (InputModifiers)KeyModifiers; public KeyModifiers KeyModifiers { get; set; } } } diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index 3b7a828b86..e79e9341a9 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -15,13 +15,6 @@ namespace Avalonia.Input { "+", Key.OemPlus }, { "-", Key.OemMinus }, { ".", Key.OemPeriod }, { ",", Key.OemComma } }; - [Obsolete("Use constructor taking KeyModifiers")] - public KeyGesture(Key key, InputModifiers modifiers) - { - Key = key; - KeyModifiers = (KeyModifiers)(((int)modifiers) & 0xf); - } - public KeyGesture(Key key, KeyModifiers modifiers = KeyModifiers.None) { Key = key; @@ -63,10 +56,7 @@ namespace Avalonia.Input } public Key Key { get; } - - [Obsolete("Use KeyModifiers")] - public InputModifiers Modifiers => (InputModifiers)KeyModifiers; - + public KeyModifiers KeyModifiers { get; } public static KeyGesture Parse(string gesture) diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs index 5f8ab24b79..055c9cf1fd 100644 --- a/src/Avalonia.Base/Input/MouseDevice.cs +++ b/src/Avalonia.Base/Input/MouseDevice.cs @@ -21,7 +21,6 @@ namespace Avalonia.Input private readonly Pointer _pointer; private bool _disposed; - private PixelPoint? _position; private MouseButton _lastMouseDownButton; public MouseDevice(Pointer? pointer = null) @@ -29,43 +28,6 @@ namespace Avalonia.Input _pointer = pointer ?? new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); } - [Obsolete("Use IPointer instead")] - public IInputElement? Captured => _pointer.Captured; - - [Obsolete("Use events instead")] - public PixelPoint Position - { - get => _position ?? new PixelPoint(-1, -1); - protected set => _position = value; - } - - [Obsolete("Use IPointer instead")] - public void Capture(IInputElement? control) - { - _pointer.Capture(control); - } - - /// - /// Gets the mouse position relative to a control. - /// - /// The control. - /// The mouse position in the control's coordinates. - public Point GetPosition(IVisual relativeTo) - { - relativeTo = relativeTo ?? throw new ArgumentNullException(nameof(relativeTo)); - - if (relativeTo.VisualRoot == null) - { - throw new InvalidOperationException("Control is not attached to visual tree."); - } - -#pragma warning disable CS0618 // Type or member is obsolete - var rootPoint = relativeTo.VisualRoot.PointToClient(Position); -#pragma warning restore CS0618 // Type or member is obsolete - var transform = relativeTo.VisualRoot.TransformToVisual(relativeTo); - return rootPoint * transform!.Value; - } - public void ProcessRawEvent(RawInputEventArgs e) { if (!e.Handled && e is RawPointerEventArgs margs) @@ -96,7 +58,6 @@ namespace Avalonia.Input if(mouse._disposed) return; - _position = e.Root.PointToScreen(e.Position); var props = CreateProperties(e); var keyModifiers = e.InputModifiers.ToKeyModifiers(); switch (e.Type) @@ -145,7 +106,6 @@ namespace Avalonia.Input private void LeaveWindow() { - _position = null; } PointerPointProperties CreateProperties(RawPointerEventArgs args) @@ -324,19 +284,7 @@ namespace Avalonia.Input _disposed = true; _pointer?.Dispose(); } - - [Obsolete] - public void TopLevelClosed(IInputRoot root) - { - // no-op - } - - [Obsolete] - public void SceneInvalidated(IInputRoot root, Rect rect) - { - // no-op - } - + public IPointer? TryGetPointer(RawPointerEventArgs ev) { return _pointer; diff --git a/src/Avalonia.Base/Input/PenDevice.cs b/src/Avalonia.Base/Input/PenDevice.cs index d22b48562c..f5f0e90a45 100644 --- a/src/Avalonia.Base/Input/PenDevice.cs +++ b/src/Avalonia.Base/Input/PenDevice.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reactive.Linq; using Avalonia.Input.Raw; using Avalonia.Platform; -using Avalonia.VisualTree; namespace Avalonia.Input { @@ -14,7 +12,6 @@ namespace Avalonia.Input public class PenDevice : IPenDevice, IDisposable { private readonly Dictionary _pointers = new(); - private readonly Dictionary _lastPositions = new(); private int _clickCount; private Rect _lastClickRect; private ulong _lastClickTime; @@ -41,9 +38,7 @@ namespace Avalonia.Input _pointers[e.RawPointerId] = pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Pen, _pointers.Count == 0); } - - _lastPositions[e.RawPointerId] = e.Root.PointToScreen(e.Position); - + var props = new PointerPointProperties(e.InputModifiers, e.Type.ToUpdateKind(), e.Point.Twist, e.Point.Pressure, e.Point.XTilt, e.Point.YTilt); var keyModifiers = e.InputModifiers.ToKeyModifiers(); @@ -69,7 +64,6 @@ namespace Avalonia.Input { pointer.Dispose(); _pointers.Remove(e.RawPointerId); - _lastPositions.Remove(e.RawPointerId); } } @@ -153,17 +147,6 @@ namespace Avalonia.Input p.Dispose(); } - [Obsolete] - IInputElement? IPointerDevice.Captured => _pointers.Values - .FirstOrDefault(p => p.IsPrimary)?.Captured; - - [Obsolete] - void IPointerDevice.Capture(IInputElement? control) => _pointers.Values - .FirstOrDefault(p => p.IsPrimary)?.Capture(control); - - [Obsolete] - Point IPointerDevice.GetPosition(IVisual relativeTo) => new Point(-1, -1); - public IPointer? TryGetPointer(RawPointerEventArgs ev) { return _pointers.TryGetValue(ev.RawPointerId, out var pointer) diff --git a/src/Avalonia.Base/Input/PointerEventArgs.cs b/src/Avalonia.Base/Input/PointerEventArgs.cs index 058c2f9cc1..1f3c726e7b 100644 --- a/src/Avalonia.Base/Input/PointerEventArgs.cs +++ b/src/Avalonia.Base/Input/PointerEventArgs.cs @@ -11,7 +11,7 @@ namespace Avalonia.Input private readonly IVisual? _rootVisual; private readonly Point _rootVisualPosition; private readonly PointerPointProperties _properties; - private Lazy?>? _previousPoints; + private readonly Lazy?>? _previousPoints; public PointerEventArgs(RoutedEvent routedEvent, IInteractive? source, @@ -43,29 +43,6 @@ namespace Avalonia.Input { _previousPoints = previousPoints; } - - - class EmulatedDevice : IPointerDevice - { - private readonly PointerEventArgs _ev; - - public EmulatedDevice(PointerEventArgs ev) - { - _ev = ev; - } - - public void ProcessRawEvent(RawInputEventArgs ev) => throw new NotSupportedException(); - - public IInputElement? Captured => _ev.Pointer.Captured; - public void Capture(IInputElement? control) - { - _ev.Pointer.Capture(control); - } - - public Point GetPosition(IVisual relativeTo) => _ev.GetPosition(relativeTo); - - public IPointer? TryGetPointer(RawPointerEventArgs ev) => _ev.Pointer; - } /// /// Gets specific pointer generated by input device. @@ -77,28 +54,6 @@ namespace Avalonia.Input /// public ulong Timestamp { get; } - private IPointerDevice? _device; - - [Obsolete("Use Pointer to get pointer-specific information")] - public IPointerDevice Device => _device ?? (_device = new EmulatedDevice(this)); - - [Obsolete("Use KeyModifiers and PointerPointProperties")] - public InputModifiers InputModifiers - { - get - { - var mods = (InputModifiers)KeyModifiers; - if (_properties.IsLeftButtonPressed) - mods |= InputModifiers.LeftMouseButton; - if (_properties.IsMiddleButtonPressed) - mods |= InputModifiers.MiddleMouseButton; - if (_properties.IsRightButtonPressed) - mods |= InputModifiers.RightMouseButton; - - return mods; - } - } - /// /// Gets a value that indicates which key modifiers were active at the time that the pointer event was initiated. /// @@ -120,9 +75,6 @@ namespace Avalonia.Input /// The pointer position in the control's coordinates. public Point GetPosition(IVisual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo); - [Obsolete("Use GetCurrentPoint")] - public PointerPoint GetPointerPoint(IVisual? relativeTo) => GetCurrentPoint(relativeTo); - /// /// Returns the PointerPoint associated with the current event /// @@ -171,8 +123,6 @@ namespace Avalonia.Input public class PointerPressedEventArgs : PointerEventArgs { - private readonly int _clickCount; - public PointerPressedEventArgs( IInteractive source, IPointer pointer, @@ -184,13 +134,10 @@ namespace Avalonia.Input : base(InputElement.PointerPressedEvent, source, pointer, rootVisual, rootVisualPosition, timestamp, properties, modifiers) { - _clickCount = clickCount; + ClickCount = clickCount; } - public int ClickCount => _clickCount; - - [Obsolete("Use PointerPressedEventArgs.GetCurrentPoint(this).Properties")] - public MouseButton MouseButton => Properties.PointerUpdateKind.GetMouseButton(); + public int ClickCount { get; } } public class PointerReleasedEventArgs : PointerEventArgs @@ -210,9 +157,6 @@ namespace Avalonia.Input /// Gets the mouse button that triggered the corresponding PointerPressed event /// public MouseButton InitialPressMouseButton { get; } - - [Obsolete("Use InitialPressMouseButton")] - public MouseButton MouseButton => InitialPressMouseButton; } public class PointerCaptureLostEventArgs : RoutedEventArgs diff --git a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs index 67d1eea7e3..a95d66346a 100644 --- a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs +++ b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs @@ -15,6 +15,8 @@ namespace Avalonia.Input _inputRoot = inputRoot ?? throw new ArgumentNullException(nameof(inputRoot)); } + public PixelPoint? LastPosition => _lastPointer?.position; + public void OnCompleted() { ClearPointerOver(); diff --git a/src/Avalonia.Base/Input/Raw/RawDragEvent.cs b/src/Avalonia.Base/Input/Raw/RawDragEvent.cs index 652bad7115..c903aad684 100644 --- a/src/Avalonia.Base/Input/Raw/RawDragEvent.cs +++ b/src/Avalonia.Base/Input/Raw/RawDragEvent.cs @@ -8,8 +8,6 @@ namespace Avalonia.Input.Raw public IDataObject Data { get; } public DragDropEffects Effects { get; set; } public RawDragEventType Type { get; } - [Obsolete("Use KeyModifiers")] - public InputModifiers Modifiers { get; } public KeyModifiers KeyModifiers { get; } public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type, @@ -21,9 +19,6 @@ namespace Avalonia.Input.Raw Data = data; Effects = effects; KeyModifiers = modifiers.ToKeyModifiers(); -#pragma warning disable CS0618 // Type or member is obsolete - Modifiers = (InputModifiers)modifiers; -#pragma warning restore CS0618 // Type or member is obsolete } } } diff --git a/src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs index 6706a45f48..3e842e6e3f 100644 --- a/src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs +++ b/src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs @@ -19,8 +19,5 @@ namespace Avalonia.Input.Raw { RawPointerId = rawPointerId; } - - [Obsolete("Use RawPointerId")] - public long TouchPointId { get => RawPointerId; set => RawPointerId = value; } } } diff --git a/src/Avalonia.Base/Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs index e914d860fd..1d5b1d6bbf 100644 --- a/src/Avalonia.Base/Input/TouchDevice.cs +++ b/src/Avalonia.Base/Input/TouchDevice.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Input.Raw; using Avalonia.Platform; -using Avalonia.VisualTree; namespace Avalonia.Input { @@ -20,9 +19,6 @@ namespace Avalonia.Input private int _clickCount; private Rect _lastClickRect; private ulong _lastClickTime; - private Pointer? _lastPointer; - - IInputElement? IPointerDevice.Captured => _lastPointer?.Captured; RawInputModifiers GetModifiers(RawInputModifiers modifiers, bool isLeftButtonDown) { @@ -32,10 +28,6 @@ namespace Avalonia.Input return rv; } - void IPointerDevice.Capture(IInputElement? control) => _lastPointer?.Capture(control); - - Point IPointerDevice.GetPosition(IVisual relativeTo) => default; - public void ProcessRawEvent(RawInputEventArgs ev) { if (ev.Handled || _disposed) @@ -51,7 +43,6 @@ namespace Avalonia.Input PointerType.Touch, _pointers.Count == 0); pointer.Capture(hit); } - _lastPointer = pointer; var target = pointer.Captured ?? args.Root; var updateKind = args.Type.ToUpdateKind(); @@ -96,7 +87,6 @@ namespace Avalonia.Input new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), keyModifier, MouseButton.Left)); } - _lastPointer = null; } if (args.Type == RawPointerEventType.TouchCancel) @@ -104,7 +94,6 @@ namespace Avalonia.Input _pointers.Remove(args.RawPointerId); using (pointer) pointer.Capture(null); - _lastPointer = null; } if (args.Type == RawPointerEventType.TouchUpdate) diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index bd0339f525..6bb94c6070 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -6,7 +6,6 @@ using Avalonia.Input.Raw; using Avalonia.Layout; using Avalonia.Metadata; using Avalonia.Rendering; -using JetBrains.Annotations; namespace Avalonia.Platform { @@ -151,13 +150,7 @@ namespace Avalonia.Platform /// Gets or sets a method called when the input focus is lost. /// Action? LostFocus { get; set; } - - /// - /// Gets a mouse device associated with toplevel - /// - [CanBeNull] - IMouseDevice MouseDevice { get; } - + IPopupImpl? CreatePopup(); /// diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs index 8d35a91e00..d8de813d47 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs @@ -45,6 +45,7 @@ Copyright © 2019 Nikita Tsukanov */ using System; +using Avalonia.Input; using Avalonia.Metadata; using Avalonia.VisualTree; using Avalonia.Media; @@ -452,16 +453,14 @@ namespace Avalonia.Controls.Primitives.PopupPositioning PopupPositionerConstraintAdjustment constraintAdjustment, Rect? rect, FlowDirection flowDirection) { - // We need a better way for tracking the last pointer position -#pragma warning disable CS0618 // Type or member is obsolete - var pointer = topLevel.PointToClient(topLevel.PlatformImpl?.MouseDevice.Position ?? default); -#pragma warning restore CS0618 // Type or member is obsolete - positionerParameters.Offset = offset; positionerParameters.ConstraintAdjustment = constraintAdjustment; if (placement == PlacementMode.Pointer) { - positionerParameters.AnchorRectangle = new Rect(pointer, new Size(1, 1)); + // We need a better way for tracking the last pointer position + var position = topLevel.PointToClient(topLevel.LastPointerPosition ?? default); + + positionerParameters.AnchorRectangle = new Rect(position, new Size(1, 1)); positionerParameters.Anchor = PopupAnchor.TopLeft; positionerParameters.Gravity = PopupGravity.BottomRight; } diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index e800f2f4b0..3bc0593813 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -12,7 +12,6 @@ using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Input; using Avalonia.Remote.Protocol.Viewport; using Avalonia.Threading; -using InputModifiers = Avalonia.Input.InputModifiers; using Key = Avalonia.Input.Key; using PixelFormat = Avalonia.Platform.PixelFormat; using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat; diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 5c5053b982..1c1df84431 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -285,6 +285,8 @@ namespace Avalonia.Controls /// public IRenderer Renderer { get; private set; } + internal PixelPoint? LastPointerPosition => _pointerOverPreProcessor?.LastPosition; + /// /// Gets the access key handler for the window. /// @@ -302,9 +304,6 @@ namespace Avalonia.Controls set { SetValue(PointerOverElementProperty, value); } } - /// - IMouseDevice? IInputRoot.MouseDevice => PlatformImpl?.MouseDevice; - /// /// Gets or sets a value indicating whether access keys are shown in the window. /// diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index 3b143d3b58..96dc929434 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -19,8 +19,10 @@ namespace Avalonia.Diagnostics.Views internal class MainWindow : Window, IStyleHost { private readonly IDisposable? _keySubscription; + private readonly IDisposable? _pointerSubscription; private readonly Dictionary _frozenPopupStates; private AvaloniaObject? _root; + private PixelPoint _lastPointerPosition; public MainWindow() { @@ -30,6 +32,10 @@ namespace Avalonia.Diagnostics.Views .OfType() .Where(x => x.Type == RawKeyEventType.KeyDown) .Subscribe(RawKeyDown); + _pointerSubscription = InputManager.Instance?.Process + .OfType() + .Subscribe(x => _lastPointerPosition = x.Root.PointToScreen(x.Position)); + _frozenPopupStates = new Dictionary(); @@ -84,6 +90,7 @@ namespace Avalonia.Diagnostics.Views { base.OnClosed(e); _keySubscription?.Dispose(); + _pointerSubscription?.Dispose(); foreach (var state in _frozenPopupStates) { @@ -108,9 +115,7 @@ namespace Avalonia.Diagnostics.Views private IControl? GetHoveredControl(TopLevel topLevel) { -#pragma warning disable CS0618 // Type or member is obsolete - var point = (topLevel as IInputRoot)?.MouseDevice?.GetPosition(topLevel) ?? default; -#pragma warning restore CS0618 // Type or member is obsolete + var point = topLevel.PointToClient(_lastPointerPosition); return (IControl?)topLevel.GetVisualsAt(point, x => { diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index d36db107e3..7435e3342e 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -101,7 +101,6 @@ namespace Avalonia.Win32.Interop.Wpf Size ITopLevelImpl.ClientSize => _finalSize; Size? ITopLevelImpl.FrameSize => null; - IMouseDevice ITopLevelImpl.MouseDevice => _mouse; double ITopLevelImpl.RenderScaling => PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1; diff --git a/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs b/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs index b1064ae25d..224ffdc3fd 100644 --- a/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs +++ b/src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs @@ -8,15 +8,28 @@ namespace Avalonia.Win32.Input { class WindowsMouseDevice : MouseDevice { - public WindowsMouseDevice() : base(new WindowsMousePointer()) + private readonly IPointer _pointer; + public WindowsMouseDevice() : base(WindowsMousePointer.CreatePointer(out var pointer)) { - + _pointer = pointer; + } + + // Normally user should use IPointer.Capture instead of MouseDevice.Capture, + // But on Windows we need to handle WM_MOUSE capture manually without having access to the Pointer. + internal void Capture(IInputElement control) + { + _pointer.Capture(control); } - class WindowsMousePointer : Pointer + internal class WindowsMousePointer : Pointer { - public WindowsMousePointer() : base(Pointer.GetNextFreeId(),PointerType.Mouse, true) + private WindowsMousePointer() : base(Pointer.GetNextFreeId(),PointerType.Mouse, true) + { + } + + public static WindowsMousePointer CreatePointer(out WindowsMousePointer pointer) { + return pointer = new WindowsMousePointer(); } protected override void PlatformCapture(IInputElement element) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 2f1a116af7..6d5cba9946 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -72,7 +72,7 @@ namespace Avalonia.Win32 private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE); private readonly TouchDevice _touchDevice; - private readonly MouseDevice _mouseDevice; + private readonly WindowsMouseDevice _mouseDevice; private readonly PenDevice _penDevice; private readonly ManagedDeferredRendererLock _rendererLock; private readonly FramebufferManager _framebuffer; @@ -689,10 +689,9 @@ namespace Avalonia.Win32 public void BeginMoveDrag(PointerPressedEventArgs e) { - _mouseDevice.Capture(null); + e.Pointer.Capture(null); DefWindowProc(_hwnd, (int)WindowsMessage.WM_NCLBUTTONDOWN, new IntPtr((int)HitTestValues.HTCAPTION), IntPtr.Zero); - e.Pointer.Capture(null); } public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e) @@ -702,7 +701,7 @@ namespace Avalonia.Win32 #if USE_MANAGED_DRAG _managedDrag.BeginResizeDrag(edge, ScreenToClient(MouseDevice.Position.ToPoint(_scaling))); #else - _mouseDevice.Capture(null); + e.Pointer.Capture(null); DefWindowProc(_hwnd, (int)WindowsMessage.WM_NCLBUTTONDOWN, new IntPtr((int)s_edgeLookup[edge]), IntPtr.Zero); #endif diff --git a/tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs index 88abb4a6fa..466aba43ee 100644 --- a/tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Media; +using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.UnitTests; using Moq; @@ -11,71 +14,79 @@ using Xunit; namespace Avalonia.Base.UnitTests.Input { - public class MouseDeviceTests + public class MouseDeviceTests : PointerTestsBase { -#pragma warning disable CS0618 // Type or member is obsolete [Fact] public void Capture_Is_Transferred_To_Parent_When_Control_Removed() { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var device = new MouseDevice(); + var impl = CreateTopLevelImplMock(renderer.Object); + Canvas control; - var root = new TestRoot + Panel rootChild; + var root = CreateInputRoot(impl.Object, rootChild = new Panel + { + Children = + { + (control = new Canvas()) + } + }); + + // Synthesize event to receive a pointer. + IPointer result = null; + root.PointerMoved += (_, a) => { - Child = control = new Canvas(), + result = a.Pointer; }; - var target = new MouseDevice(); + SetHit(renderer, control); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); - target.Capture(control); - Assert.Same(control, target.Captured); + Assert.NotNull(result); + + result.Capture(control); + Assert.Same(control, result.Captured); - root.Child = null; + rootChild.Children.Clear(); - Assert.Same(root, target.Captured); + Assert.Same(rootChild, result.Captured); } -#pragma warning restore CS0618 // Type or member is obsolete [Fact] public void GetPosition_Should_Respect_Control_RenderTransform() { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + var renderer = new Mock(); + var device = new MouseDevice(); + var impl = CreateTopLevelImplMock(renderer.Object); - using (TestApplication(renderer.Object)) + Border border; + var root = CreateInputRoot(impl.Object, new Panel { - var inputManager = InputManager.Instance; - - var root = new TestRoot + Children = { - MouseDevice = new MouseDevice(), - Child = new Border + (border = new Border { Background = Brushes.Black, RenderTransform = new TranslateTransform(10, 0), - } - }; - - SendMouseMove(inputManager, root, new Point(11, 11)); - -#pragma warning disable CS0618 // Type or member is obsolete - var result = root.MouseDevice.GetPosition(root.Child); -#pragma warning restore CS0618 // Type or member is obsolete - Assert.Equal(new Point(1, 11), result); - } - } + }) + } + }); + + + Point? result = null; + root.PointerMoved += (_, a) => + { + result = a.GetPosition(border); + }; - private void SendMouseMove(IInputManager inputManager, TestRoot root, Point p = new Point()) - { - inputManager.ProcessInput(new RawPointerEventArgs( - root.MouseDevice, - 0, - root, - RawPointerEventType.Move, - p, - RawInputModifiers.None)); - } + SetHit(renderer, border); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root, new Point(11, 11))); - private IDisposable TestApplication(IRenderer renderer) - { - return UnitTestApplication.Start( - new TestServices(inputManager: new InputManager())); + Assert.Equal(new Point(1, 11), result); } } } diff --git a/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs b/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs index a5ca2aef4a..a83b176484 100644 --- a/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs @@ -3,14 +3,10 @@ using System; using System.Collections.Generic; using Avalonia.Controls; -using Avalonia.Controls.Presenters; -using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Input.Raw; -using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.UnitTests; -using Avalonia.VisualTree; using Moq; @@ -18,7 +14,7 @@ using Xunit; namespace Avalonia.Base.UnitTests.Input { - public class PointerOverTests + public class PointerOverTests : PointerTestsBase { // https://github.com/AvaloniaUI/Avalonia/issues/2821 [Fact] @@ -448,87 +444,5 @@ namespace Avalonia.Base.UnitTests.Input c.PointerMoved += handler; } } - - private static void SetHit(Mock renderer, IControl? hit) - { - renderer.Setup(x => x.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(hit is null ? Array.Empty() : new[] { hit }); - - renderer.Setup(x => x.HitTestFirst(It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(hit); - } - - private static void SetMove(Mock deviceMock, IInputRoot root, IInputElement element) - { - deviceMock.Setup(d => d.ProcessRawEvent(It.IsAny())) - .Callback(() => element.RaiseEvent(CreatePointerMovedArgs(root, element))); - } - - private static Mock CreateTopLevelImplMock(IRenderer renderer) - { - var impl = new Mock(); - impl.DefaultValue = DefaultValue.Mock; - impl.SetupAllProperties(); - impl.SetupGet(r => r.RenderScaling).Returns(1); - impl.Setup(r => r.CreateRenderer(It.IsAny())).Returns(renderer); - impl.Setup(r => r.PointToScreen(It.IsAny())).Returns(p => new PixelPoint((int)p.X, (int)p.Y)); - impl.Setup(r => r.PointToClient(It.IsAny())).Returns(p => new Point(p.X, p.Y)); - return impl; - } - - private static IInputRoot CreateInputRoot(IWindowImpl impl, IControl child) - { - var root = new Window(impl) - { - Width = 100, - Height = 100, - Content = child, - Template = new FuncControlTemplate((w, _) => new ContentPresenter - { - Content = w.Content - }) - }; - root.Show(); - return root; - } - - private static IInputRoot CreateInputRoot(IRenderer renderer, IControl child) - { - return CreateInputRoot(CreateTopLevelImplMock(renderer).Object, child); - } - - private static RawPointerEventArgs CreateRawPointerMovedArgs( - IPointerDevice pointerDevice, - IInputRoot root, - Point? positition = null) - { - return new RawPointerEventArgs(pointerDevice, 0, root, RawPointerEventType.Move, - positition ?? default, default); - } - - private static PointerEventArgs CreatePointerMovedArgs( - IInputRoot root, IInputElement? source, Point? positition = null) - { - return new PointerEventArgs(InputElement.PointerMovedEvent, source, new Mock().Object, root, - positition ?? default, default, PointerPointProperties.None, KeyModifiers.None); - } - - private static Mock CreatePointerDeviceMock( - IPointer? pointer = null, - PointerType pointerType = PointerType.Mouse) - { - if (pointer is null) - { - var pointerMock = new Mock(); - pointerMock.SetupGet(p => p.Type).Returns(pointerType); - pointer = pointerMock.Object; - } - - var pointerDevice = new Mock(); - pointerDevice.Setup(d => d.TryGetPointer(It.IsAny())) - .Returns(pointer); - - return pointerDevice; - } } } diff --git a/tests/Avalonia.Base.UnitTests/Input/PointerTestsBase.cs b/tests/Avalonia.Base.UnitTests/Input/PointerTestsBase.cs new file mode 100644 index 0000000000..1ca6678aef --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Input/PointerTestsBase.cs @@ -0,0 +1,90 @@ +#nullable enable +using System; +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Platform; +using Avalonia.Rendering; +using Avalonia.VisualTree; +using Moq; + +namespace Avalonia.Base.UnitTests.Input; + +public abstract class PointerTestsBase +{ + protected static void SetHit(Mock renderer, IControl? hit) + { + renderer.Setup(x => x.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(hit is null ? Array.Empty() : new[] { hit }); + + renderer.Setup(x => x.HitTestFirst(It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(hit); + } + + protected static void SetMove(Mock deviceMock, IInputRoot root, IInputElement element) + { + deviceMock.Setup(d => d.ProcessRawEvent(It.IsAny())) + .Callback(() => element.RaiseEvent(CreatePointerMovedArgs(root, element))); + } + + protected static Mock CreateTopLevelImplMock(IRenderer renderer) + { + var impl = new Mock(); + impl.DefaultValue = DefaultValue.Mock; + impl.SetupAllProperties(); + impl.SetupGet(r => r.RenderScaling).Returns(1); + impl.Setup(r => r.CreateRenderer(It.IsAny())).Returns(renderer); + impl.Setup(r => r.PointToScreen(It.IsAny())).Returns(p => new PixelPoint((int)p.X, (int)p.Y)); + impl.Setup(r => r.PointToClient(It.IsAny())).Returns(p => new Point(p.X, p.Y)); + return impl; + } + + protected static IInputRoot CreateInputRoot(IWindowImpl impl, IControl child) + { + var root = new Window(impl) + { + Width = 100, + Height = 100, + Content = child, + Template = new FuncControlTemplate((w, _) => new ContentPresenter { Content = w.Content }) + }; + root.Show(); + return root; + } + + protected static RawPointerEventArgs CreateRawPointerMovedArgs( + IPointerDevice pointerDevice, + IInputRoot root, + Point? positition = null) + { + return new RawPointerEventArgs(pointerDevice, 0, root, RawPointerEventType.Move, + positition ?? default, default); + } + + protected static PointerEventArgs CreatePointerMovedArgs( + IInputRoot root, IInputElement? source, Point? positition = null) + { + return new PointerEventArgs(InputElement.PointerMovedEvent, source, new Mock().Object, root, + positition ?? default, default, PointerPointProperties.None, KeyModifiers.None); + } + + protected static Mock CreatePointerDeviceMock( + IPointer? pointer = null, + PointerType pointerType = PointerType.Mouse) + { + if (pointer is null) + { + var pointerMock = new Mock(); + pointerMock.SetupGet(p => p.Type).Returns(pointerType); + pointer = pointerMock.Object; + } + + var pointerDevice = new Mock(); + pointerDevice.Setup(d => d.TryGetPointer(It.IsAny())) + .Returns(pointer); + + return pointerDevice; + } +} diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index 339b87cf88..e30f0fa5f3 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -34,7 +34,6 @@ namespace Avalonia.UnitTests windowImpl.Setup(x => x.RenderScaling).Returns(1); windowImpl.Setup(x => x.Screen).Returns(CreateScreenMock().Object); windowImpl.Setup(x => x.Position).Returns(() => position); - SetupToplevel(windowImpl); windowImpl.Setup(x => x.CreatePopup()).Returns(() => { @@ -100,8 +99,6 @@ namespace Avalonia.UnitTests { popupImpl.Object.Closed?.Invoke(); }); - - SetupToplevel(popupImpl); return popupImpl; } @@ -144,10 +141,5 @@ namespace Avalonia.UnitTests { return null; } - - private static void SetupToplevel(Mock mock) where T : class, ITopLevelImpl - { - mock.SetupGet(x => x.MouseDevice).Returns(new MouseDevice()); - } } } diff --git a/tests/Avalonia.UnitTests/TestRoot.cs b/tests/Avalonia.UnitTests/TestRoot.cs index 41e29a85c4..138958512f 100644 --- a/tests/Avalonia.UnitTests/TestRoot.cs +++ b/tests/Avalonia.UnitTests/TestRoot.cs @@ -58,9 +58,7 @@ namespace Avalonia.UnitTests public IKeyboardNavigationHandler KeyboardNavigationHandler => null; public IInputElement PointerOverElement { get; set; } - - public IMouseDevice MouseDevice { get; set; } - + public bool ShowAccessKeys { get; set; } public IStyleHost StylingParent { get; set; }