csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
11 KiB
251 lines
11 KiB
using System;
|
|
using System.Collections.Generic;
|
|
|
|
using Android.Views;
|
|
|
|
using Avalonia.Android.Platform.SkiaPlatform;
|
|
using Avalonia.Collections.Pooled;
|
|
using Avalonia.Input;
|
|
using Avalonia.Input.Raw;
|
|
|
|
#nullable enable
|
|
|
|
namespace Avalonia.Android.Platform.Specific.Helpers
|
|
{
|
|
internal class AndroidMotionEventsHelper : IDisposable
|
|
{
|
|
private static readonly PooledList<RawPointerPoint> s_intermediatePointsPooledList = new(ClearMode.Never);
|
|
private const float RadiansToDegree = (float)(180f * Math.PI);
|
|
private readonly TouchDevice _touchDevice;
|
|
private readonly MouseDevice _mouseDevice;
|
|
private readonly PenDevice _penDevice;
|
|
private readonly TopLevelImpl _view;
|
|
private bool _disposed;
|
|
|
|
public AndroidMotionEventsHelper(TopLevelImpl view)
|
|
{
|
|
_touchDevice = new TouchDevice();
|
|
_penDevice = new PenDevice();
|
|
_mouseDevice = new MouseDevice();
|
|
_view = view;
|
|
}
|
|
|
|
public bool? DispatchMotionEvent(MotionEvent e, out bool callBase)
|
|
{
|
|
callBase = true;
|
|
if (_disposed)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var eventTime = (ulong)DateTime.Now.Millisecond;
|
|
var inputRoot = _view.InputRoot;
|
|
var actionMasked = e.ActionMasked;
|
|
var modifiers = GetModifiers(e.MetaState, e.ButtonState);
|
|
|
|
if (actionMasked == MotionEventActions.Move)
|
|
{
|
|
for (int index = 0; index < e.PointerCount; index++)
|
|
{
|
|
var toolType = e.GetToolType(index);
|
|
var device = GetDevice(toolType);
|
|
var eventType = toolType == MotionEventToolType.Finger ? RawPointerEventType.TouchUpdate : RawPointerEventType.Move;
|
|
var point = CreatePoint(e, index);
|
|
modifiers |= GetToolModifiers(toolType);
|
|
|
|
// ButtonState reports only mouse buttons, but not touch or stylus pointer.
|
|
if (toolType != MotionEventToolType.Mouse)
|
|
{
|
|
modifiers |= RawInputModifiers.LeftMouseButton;
|
|
}
|
|
|
|
var args = new RawTouchEventArgs(device, eventTime, inputRoot, eventType, point, modifiers, e.GetPointerId(index))
|
|
{
|
|
IntermediatePoints = new Lazy<IReadOnlyList<RawPointerPoint>?>(() =>
|
|
{
|
|
var site = e.HistorySize;
|
|
s_intermediatePointsPooledList.Clear();
|
|
s_intermediatePointsPooledList.Capacity = site;
|
|
|
|
for (int pos = 0; pos < site; pos++)
|
|
{
|
|
s_intermediatePointsPooledList.Add(CreateHistoricalPoint(e, index, pos));
|
|
}
|
|
|
|
return s_intermediatePointsPooledList;
|
|
})
|
|
};
|
|
_view.Input(args);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var index = e.ActionIndex;
|
|
var toolType = e.GetToolType(index);
|
|
var device = GetDevice(toolType);
|
|
modifiers |= GetToolModifiers(toolType);
|
|
var point = CreatePoint(e, index);
|
|
|
|
if (actionMasked == MotionEventActions.Scroll && toolType == MotionEventToolType.Mouse)
|
|
{
|
|
var delta = new Vector(e.GetAxisValue(Axis.Hscroll), e.GetAxisValue(Axis.Vscroll));
|
|
var args = new RawMouseWheelEventArgs(device, eventTime, inputRoot, point.Position, delta, RawInputModifiers.None);
|
|
_view.Input(args);
|
|
}
|
|
else
|
|
{
|
|
var eventType = GetActionType(e, actionMasked, toolType);
|
|
if (eventType >= 0)
|
|
{
|
|
var args = new RawTouchEventArgs(device, eventTime, inputRoot, eventType, point, modifiers, e.GetPointerId(index));
|
|
_view.Input(args);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static RawInputModifiers GetModifiers(MetaKeyStates metaState, MotionEventButtonState buttonState)
|
|
{
|
|
var modifiers = RawInputModifiers.None;
|
|
if (metaState.HasAnyFlag(MetaKeyStates.ShiftOn))
|
|
{
|
|
modifiers |= RawInputModifiers.Shift;
|
|
}
|
|
if (metaState.HasAnyFlag(MetaKeyStates.CtrlOn))
|
|
{
|
|
modifiers |= RawInputModifiers.Control;
|
|
}
|
|
if (metaState.HasAnyFlag(MetaKeyStates.AltOn))
|
|
{
|
|
modifiers |= RawInputModifiers.Alt;
|
|
}
|
|
if (metaState.HasAnyFlag(MetaKeyStates.MetaOn))
|
|
{
|
|
modifiers |= RawInputModifiers.Meta;
|
|
}
|
|
if (buttonState.HasAnyFlag(MotionEventButtonState.Primary))
|
|
{
|
|
modifiers |= RawInputModifiers.LeftMouseButton;
|
|
}
|
|
if (buttonState.HasAnyFlag(MotionEventButtonState.Secondary))
|
|
{
|
|
modifiers |= RawInputModifiers.RightMouseButton;
|
|
}
|
|
if (buttonState.HasAnyFlag(MotionEventButtonState.Tertiary))
|
|
{
|
|
modifiers |= RawInputModifiers.MiddleMouseButton;
|
|
}
|
|
if (buttonState.HasAnyFlag(MotionEventButtonState.Back))
|
|
{
|
|
modifiers |= RawInputModifiers.XButton1MouseButton;
|
|
}
|
|
if (buttonState.HasAnyFlag(MotionEventButtonState.Forward))
|
|
{
|
|
modifiers |= RawInputModifiers.XButton2MouseButton;
|
|
}
|
|
if (buttonState.HasAnyFlag(MotionEventButtonState.StylusPrimary))
|
|
{
|
|
modifiers |= RawInputModifiers.PenBarrelButton;
|
|
}
|
|
return modifiers;
|
|
}
|
|
|
|
#pragma warning disable CA1416 // Validate platform compatibility
|
|
private static RawPointerEventType GetActionType(MotionEvent e, MotionEventActions actionMasked, MotionEventToolType toolType)
|
|
{
|
|
var isTouch = toolType == MotionEventToolType.Finger;
|
|
var isMouse = toolType == MotionEventToolType.Mouse;
|
|
switch (actionMasked)
|
|
{
|
|
// DOWN
|
|
case MotionEventActions.Down when !isMouse:
|
|
case MotionEventActions.PointerDown when !isMouse:
|
|
return isTouch ? RawPointerEventType.TouchBegin : RawPointerEventType.LeftButtonDown;
|
|
case MotionEventActions.ButtonPress:
|
|
return e.ActionButton switch
|
|
{
|
|
MotionEventButtonState.Back => RawPointerEventType.XButton1Down,
|
|
MotionEventButtonState.Forward => RawPointerEventType.XButton2Down,
|
|
MotionEventButtonState.Primary => RawPointerEventType.LeftButtonDown,
|
|
MotionEventButtonState.Secondary => RawPointerEventType.RightButtonDown,
|
|
MotionEventButtonState.StylusPrimary => RawPointerEventType.LeftButtonDown,
|
|
MotionEventButtonState.StylusSecondary => RawPointerEventType.RightButtonDown,
|
|
MotionEventButtonState.Tertiary => RawPointerEventType.MiddleButtonDown,
|
|
_ => RawPointerEventType.LeftButtonDown
|
|
};
|
|
// UP
|
|
case MotionEventActions.Up when !isMouse:
|
|
case MotionEventActions.PointerUp when !isMouse:
|
|
return isTouch ? RawPointerEventType.TouchEnd : RawPointerEventType.LeftButtonUp;
|
|
case MotionEventActions.ButtonRelease:
|
|
return e.ActionButton switch
|
|
{
|
|
MotionEventButtonState.Back => RawPointerEventType.XButton1Up,
|
|
MotionEventButtonState.Forward => RawPointerEventType.XButton2Up,
|
|
MotionEventButtonState.Primary => RawPointerEventType.LeftButtonUp,
|
|
MotionEventButtonState.Secondary => RawPointerEventType.RightButtonUp,
|
|
MotionEventButtonState.StylusPrimary => RawPointerEventType.LeftButtonUp,
|
|
MotionEventButtonState.StylusSecondary => RawPointerEventType.RightButtonUp,
|
|
MotionEventButtonState.Tertiary => RawPointerEventType.MiddleButtonUp,
|
|
_ => RawPointerEventType.LeftButtonUp
|
|
};
|
|
// MOVE
|
|
case MotionEventActions.Outside:
|
|
case MotionEventActions.HoverMove:
|
|
case MotionEventActions.Move:
|
|
return isTouch ? RawPointerEventType.TouchUpdate : RawPointerEventType.Move;
|
|
// CANCEL
|
|
case MotionEventActions.Cancel:
|
|
return isTouch ? RawPointerEventType.TouchCancel : RawPointerEventType.LeaveWindow;
|
|
default:
|
|
return (RawPointerEventType)(-1);
|
|
}
|
|
}
|
|
#pragma warning restore CA1416 // Validate platform compatibility
|
|
|
|
private IPointerDevice GetDevice(MotionEventToolType type)
|
|
{
|
|
return type switch
|
|
{
|
|
MotionEventToolType.Mouse => _mouseDevice,
|
|
MotionEventToolType.Stylus => _penDevice,
|
|
MotionEventToolType.Eraser => _penDevice,
|
|
MotionEventToolType.Finger => _touchDevice,
|
|
_ => _touchDevice
|
|
};
|
|
}
|
|
|
|
private RawPointerPoint CreatePoint(MotionEvent e, int index)
|
|
{
|
|
return new RawPointerPoint
|
|
{
|
|
Position = new Point(e.GetX(index), e.GetY(index)) / _view.RenderScaling,
|
|
Pressure = Math.Min(e.GetPressure(index), 1), // android pressure can depend on the device, can be mixed up with "GetSize", may be larger than 1.0f on some devices
|
|
Twist = e.GetOrientation(index) * RadiansToDegree
|
|
};
|
|
}
|
|
|
|
private RawPointerPoint CreateHistoricalPoint(MotionEvent e, int index, int pos)
|
|
{
|
|
return new RawPointerPoint
|
|
{
|
|
Position = new Point(e.GetHistoricalX(index, pos), e.GetHistoricalY(index, pos)) / _view.RenderScaling,
|
|
Pressure = Math.Min(e.GetHistoricalPressure(index, pos), 1),
|
|
Twist = e.GetHistoricalOrientation(index, pos) * RadiansToDegree
|
|
};
|
|
}
|
|
|
|
private static RawInputModifiers GetToolModifiers(MotionEventToolType toolType)
|
|
{
|
|
// Android "Eraser" indicates Inverted pen OR actual Eraser. So we have to go both here.
|
|
return toolType == MotionEventToolType.Eraser ? RawInputModifiers.PenInverted | RawInputModifiers.PenEraser : RawInputModifiers.None;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_disposed = true;
|
|
}
|
|
}
|
|
}
|
|
|