diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Input/IKeyboardDevice.cs index 9506dc36fb..b3ea7c5f4f 100644 --- a/src/Avalonia.Input/IKeyboardDevice.cs +++ b/src/Avalonia.Input/IKeyboardDevice.cs @@ -47,6 +47,8 @@ namespace Avalonia.Input MiddleMouseButton = 64, XButton1MouseButton = 128, XButton2MouseButton = 256, + BarrelPenButton = 512, + PenEraser = 1024, KeyboardMask = Alt | Control | Shift | Meta } diff --git a/src/Avalonia.Input/IPointer.cs b/src/Avalonia.Input/IPointer.cs index 7af48cef82..361f3ac370 100644 --- a/src/Avalonia.Input/IPointer.cs +++ b/src/Avalonia.Input/IPointer.cs @@ -13,6 +13,7 @@ namespace Avalonia.Input public enum PointerType { Mouse, - Touch + Touch, + Pen } } diff --git a/src/Avalonia.Input/PenDevice.cs b/src/Avalonia.Input/PenDevice.cs index dd3d60ebb9..3c0f3c7709 100644 --- a/src/Avalonia.Input/PenDevice.cs +++ b/src/Avalonia.Input/PenDevice.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Reactive.Linq; using Avalonia.Input.Raw; using Avalonia.Interactivity; -using Avalonia.Platform; using Avalonia.VisualTree; namespace Avalonia.Input @@ -13,17 +12,13 @@ namespace Avalonia.Input /// public class PenDevice : IPenDevice, IDisposable { - private int _clickCount; - private Rect _lastClickRect; - private ulong _lastClickTime; - private readonly Pointer _pointer; private bool _disposed; private PixelPoint? _position; public PenDevice(Pointer? pointer = null) { - _pointer = pointer ?? new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); + _pointer = pointer ?? new Pointer(Pointer.GetNextFreeId(), PointerType.Pen, true); } /// @@ -37,16 +32,6 @@ namespace Avalonia.Input [Obsolete("Use IPointer instead")] public IInputElement? Captured => _pointer.Captured; - /// - /// Gets the mouse position, in screen coordinates. - /// - [Obsolete("Use events instead")] - public PixelPoint Position - { - get => _position ?? new PixelPoint(-1, -1); - protected set => _position = value; - } - /// /// Captures mouse input to the specified control. /// @@ -76,7 +61,7 @@ namespace Avalonia.Input } #pragma warning disable CS0618 // Type or member is obsolete - var rootPoint = relativeTo.VisualRoot.PointToClient(Position); + var rootPoint = relativeTo.VisualRoot.PointToClient(_position ?? new PixelPoint(-1, -1)); #pragma warning restore CS0618 // Type or member is obsolete var transform = relativeTo.VisualRoot.TransformToVisual(relativeTo); return rootPoint * transform!.Value; @@ -120,29 +105,13 @@ namespace Avalonia.Input } } } - - int ButtonCount(PointerPointProperties props) - { - var rv = 0; - if (props.IsLeftButtonPressed) - rv++; - if (props.IsMiddleButtonPressed) - rv++; - if (props.IsRightButtonPressed) - rv++; - if (props.IsXButton1Pressed) - rv++; - if (props.IsXButton2Pressed) - rv++; - return rv; - } private void ProcessRawEvent(RawPointerEventArgs e) { e = e ?? throw new ArgumentNullException(nameof(e)); - var mouse = (PenDevice)e.Device; - if(mouse._disposed) + var pen = (PenDevice)e.Device; + if(pen._disposed) return; _position = e.Root.PointToScreen(e.Position); @@ -151,34 +120,16 @@ namespace Avalonia.Input switch (e.Type) { case RawPointerEventType.LeaveWindow: - LeaveWindow(mouse, e.Timestamp, e.Root, props, keyModifiers); + LeaveWindow(pen, e.Timestamp, e.Root, props, keyModifiers); break; - case RawPointerEventType.LeftButtonDown: - case RawPointerEventType.RightButtonDown: - case RawPointerEventType.MiddleButtonDown: - case RawPointerEventType.XButton1Down: - case RawPointerEventType.XButton2Down: - if (ButtonCount(props) > 1) - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); - else - e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position, - props, keyModifiers); + case RawPointerEventType.PenBegin: + e.Handled = PenDown(pen, e.Timestamp, e.Root, e.Position, props, keyModifiers); break; - case RawPointerEventType.LeftButtonUp: - case RawPointerEventType.RightButtonUp: - case RawPointerEventType.MiddleButtonUp: - case RawPointerEventType.XButton1Up: - case RawPointerEventType.XButton2Up: - if (ButtonCount(props) != 0) - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); - else - e.Handled = MouseUp(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); + case RawPointerEventType.PenEnd: + e.Handled = PenUp(pen, e.Timestamp, e.Root, e.Position, props, keyModifiers); break; - case RawPointerEventType.Move: - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); - break; - case RawPointerEventType.Wheel: - e.Handled = MouseWheel(mouse, e.Timestamp, e.Root, e.Position, props, ((RawMouseWheelEventArgs)e).Delta, keyModifiers); + case RawPointerEventType.PenUpdate: + e.Handled = PenMove(pen, e.Timestamp, e.Root, e.Position, props, keyModifiers); break; } } @@ -196,35 +147,18 @@ namespace Avalonia.Input PointerPointProperties CreateProperties(RawPointerEventArgs args) { - var kind = PointerUpdateKind.Other; - if (args.Type == RawPointerEventType.LeftButtonDown) + if (args.Type == RawPointerEventType.PenBegin) kind = PointerUpdateKind.LeftButtonPressed; - if (args.Type == RawPointerEventType.MiddleButtonDown) - kind = PointerUpdateKind.MiddleButtonPressed; - if (args.Type == RawPointerEventType.RightButtonDown) - kind = PointerUpdateKind.RightButtonPressed; - if (args.Type == RawPointerEventType.XButton1Down) - kind = PointerUpdateKind.XButton1Pressed; - if (args.Type == RawPointerEventType.XButton2Down) - kind = PointerUpdateKind.XButton2Pressed; - if (args.Type == RawPointerEventType.LeftButtonUp) + if (args.Type == RawPointerEventType.PenEnd) kind = PointerUpdateKind.LeftButtonReleased; - if (args.Type == RawPointerEventType.MiddleButtonUp) - kind = PointerUpdateKind.MiddleButtonReleased; - if (args.Type == RawPointerEventType.RightButtonUp) - kind = PointerUpdateKind.RightButtonReleased; - if (args.Type == RawPointerEventType.XButton1Up) - kind = PointerUpdateKind.XButton1Released; - if (args.Type == RawPointerEventType.XButton2Up) - kind = PointerUpdateKind.XButton2Released; return new PointerPointProperties(args.InputModifiers, kind); } private MouseButton _lastMouseDownButton; - private bool MouseDown(IPenDevice device, ulong timestamp, IInputElement root, Point p, + private bool PenDown(IPenDevice device, ulong timestamp, IInputElement root, Point p, PointerPointProperties properties, KeyModifiers inputModifiers) { @@ -239,21 +173,8 @@ namespace Avalonia.Input var source = GetSource(hit); if (source != null) { - var settings = AvaloniaLocator.Current.GetService(); - var doubleClickTime = settings?.DoubleClickTime.TotalMilliseconds ?? 500; - var doubleClickSize = settings?.DoubleClickSize ?? new Size(4, 4); - - if (!_lastClickRect.Contains(p) || timestamp - _lastClickTime > doubleClickTime) - { - _clickCount = 0; - } - - ++_clickCount; - _lastClickTime = timestamp; - _lastClickRect = new Rect(p, new Size()) - .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); _lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton(); - var e = new PointerPressedEventArgs(source, _pointer, root, p, timestamp, properties, inputModifiers, _clickCount); + var e = new PointerPressedEventArgs(source, _pointer, root, p, timestamp, properties, inputModifiers, 1); source.RaiseEvent(e); return e.Handled; } @@ -262,7 +183,7 @@ namespace Avalonia.Input return false; } - private bool MouseMove(IPenDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties properties, + private bool PenMove(IPenDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties properties, KeyModifiers inputModifiers) { device = device ?? throw new ArgumentNullException(nameof(device)); @@ -292,7 +213,7 @@ namespace Avalonia.Input return false; } - private bool MouseUp(IPenDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties props, + private bool PenUp(IPenDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties props, KeyModifiers inputModifiers) { device = device ?? throw new ArgumentNullException(nameof(device)); @@ -314,27 +235,6 @@ namespace Avalonia.Input return false; } - private bool MouseWheel(IPenDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, - Vector delta, KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var hit = HitTest(root, p); - var source = GetSource(hit); - - if (source is not null) - { - var e = new PointerWheelEventArgs(source, _pointer, root, p, timestamp, props, inputModifiers, delta); - - source?.RaiseEvent(e); - return e.Handled; - } - - return false; - } - private IInteractive? GetSource(IVisual? hit) { if (hit is null) diff --git a/src/Avalonia.Input/PointerPoint.cs b/src/Avalonia.Input/PointerPoint.cs index 9f8285a8e1..16fab7410c 100644 --- a/src/Avalonia.Input/PointerPoint.cs +++ b/src/Avalonia.Input/PointerPoint.cs @@ -20,6 +20,14 @@ namespace Avalonia.Input public bool IsRightButtonPressed { get; } public bool IsXButton1Pressed { get; } public bool IsXButton2Pressed { get; } + public bool IsBarrelButtonPressed { get; } + public bool IsEraser { get; } + + public float Twist { get; } + public float Pressure { get; } + public float XTilt { get; } + public float YTilt { get; } + public PointerUpdateKind PointerUpdateKind { get; } @@ -36,10 +44,12 @@ namespace Avalonia.Input IsRightButtonPressed = modifiers.HasAllFlags(RawInputModifiers.RightMouseButton); IsXButton1Pressed = modifiers.HasAllFlags(RawInputModifiers.XButton1MouseButton); IsXButton2Pressed = modifiers.HasAllFlags(RawInputModifiers.XButton2MouseButton); + IsBarrelButtonPressed = modifiers.HasAllFlags(RawInputModifiers.BarrelPenButton); + IsEraser = modifiers.HasAllFlags(RawInputModifiers.PenEraser); // The underlying input source might be reporting the previous state, // so make sure that we reflect the current state - + if (kind == PointerUpdateKind.LeftButtonPressed) IsLeftButtonPressed = true; if (kind == PointerUpdateKind.LeftButtonReleased) @@ -60,6 +70,20 @@ namespace Avalonia.Input IsXButton2Pressed = true; if (kind == PointerUpdateKind.XButton2Released) IsXButton2Pressed = false; + if (kind == PointerUpdateKind.BarrelButtonPressed) + IsBarrelButtonPressed = true; + if (kind == PointerUpdateKind.BarrelButtonReleased) + IsBarrelButtonPressed = false; + } + + public PointerPointProperties(RawInputModifiers modifiers, PointerUpdateKind kind, + float twist, float pressure, float xTilt, float yTilt + ) : this (modifiers, kind) + { + Twist = twist; + Pressure = pressure; + XTilt = xTilt; + YTilt = yTilt; } public static PointerPointProperties None { get; } = new PointerPointProperties(); @@ -77,7 +101,9 @@ namespace Avalonia.Input RightButtonReleased, XButton1Released, XButton2Released, - Other + Other, + BarrelButtonPressed, + BarrelButtonReleased } public static class PointerUpdateKindExtensions diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs index 62a1dd5d84..ea160efef5 100644 --- a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs @@ -21,7 +21,13 @@ namespace Avalonia.Input.Raw TouchBegin, TouchUpdate, TouchEnd, - TouchCancel + TouchCancel, + PenBegin, + PenUpdate, + PenEnd, + PenCancel, + BarrelUp, + BarrelDown, } /// diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 5e31bba925..db11508ed7 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -374,17 +374,6 @@ namespace Avalonia.Win32 break; } - - - - - - - - - - - case WindowsMessage.WM_POINTERDEVICECHANGE: { //notifies about changes in the settings of a monitor that has a digitizer attached to it. @@ -414,7 +403,10 @@ namespace Avalonia.Win32 GetDeviceInfo(wParam, out var device, out var info); var point = PointToClient(PointFromLParam(lParam)); var modifiers = GetInputModifiers(info.dwKeyStates); - var eventType = info.ButtonChangeType switch + + if (info.ButtonChangeType != PointerButtonChangeType.POINTER_CHANGE_NONE) + { + var eventType = info.ButtonChangeType switch { PointerButtonChangeType.POINTER_CHANGE_FIRSTBUTTON_DOWN => RawPointerEventType.LeftButtonDown, PointerButtonChangeType.POINTER_CHANGE_SECONDBUTTON_DOWN => RawPointerEventType.RightButtonDown, @@ -428,12 +420,13 @@ namespace Avalonia.Win32 PointerButtonChangeType.POINTER_CHANGE_FOURTHBUTTON_UP => RawPointerEventType.XButton1Up, PointerButtonChangeType.POINTER_CHANGE_FIFTHBUTTON_UP => RawPointerEventType.XButton2Up, }; - if (eventType == RawPointerEventType.NonClientLeftButtonDown && - (WindowsMessage)msg == WindowsMessage.WM_NCPOINTERDOWN) - { - eventType = RawPointerEventType.NonClientLeftButtonDown; + if (eventType == RawPointerEventType.NonClientLeftButtonDown && + (WindowsMessage)msg == WindowsMessage.WM_NCPOINTERDOWN) + { + eventType = RawPointerEventType.NonClientLeftButtonDown; + } + e = new RawPointerEventArgs(device, timestamp, _owner, eventType, point, modifiers); } - e = new RawPointerEventArgs(device, timestamp, _owner, eventType, point, modifiers); break; } case WindowsMessage.WM_POINTERUPDATE: @@ -446,7 +439,8 @@ namespace Avalonia.Win32 var point = PointToClient(PointFromLParam(lParam)); var modifiers = GetInputModifiers(info.dwKeyStates); - e = new RawPointerEventArgs(device, timestamp, _owner, RawPointerEventType.Move, point, modifiers); + e = new RawPointerEventArgs( + device, timestamp, _owner, RawPointerEventType.Move, point, modifiers); break; } case WindowsMessage.WM_POINTERENTER: @@ -462,14 +456,10 @@ namespace Avalonia.Win32 { GetDeviceInfo(wParam, out var device, out var info); var point = PointToClient(PointFromLParam(lParam)); + var modifiers = GetInputModifiers(info.dwKeyStates); e = new RawPointerEventArgs( - device, - timestamp, - _owner, - RawPointerEventType.LeaveWindow, - point, - WindowsKeyboardDevice.Instance.Modifiers); + device, timestamp, _owner, RawPointerEventType.LeaveWindow, point, modifiers); break; } case WindowsMessage.WM_POINTERACTIVATE: @@ -520,33 +510,11 @@ namespace Avalonia.Win32 } case WindowsMessage.WM_PARENTNOTIFY: { - //This message is sent in a dialog scenarios. Contains mouse position in an old-way, - //but listed in the wm_pointer reference + //This message is sent in a dialog scenarios. Contains mouse position. + //Old message, but listed in the wm_pointer reference //https://docs.microsoft.com/en-us/previous-versions/windows/desktop/inputmsg/wm-parentnotify break; } - - - - - - - - - - - - - - - - - - - - - - case WindowsMessage.WM_NCPAINT: { if (!HasFullDecorations)