committed by
GitHub
22 changed files with 650 additions and 379 deletions
@ -0,0 +1,251 @@ |
|||
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 static readonly float s_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) * s_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) * s_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; |
|||
} |
|||
} |
|||
} |
|||
@ -1,85 +0,0 @@ |
|||
using System; |
|||
using Android.Views; |
|||
using Avalonia.Input; |
|||
using Avalonia.Input.Raw; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Android.Platform.Specific.Helpers |
|||
{ |
|||
public class AndroidTouchEventsHelper<TView> : IDisposable where TView : ITopLevelImpl, IAndroidView |
|||
{ |
|||
private TView _view; |
|||
public bool HandleEvents { get; set; } |
|||
|
|||
public AndroidTouchEventsHelper(TView view, Func<IInputRoot> getInputRoot, Func<MotionEvent, int, Point> getPointfunc) |
|||
{ |
|||
this._view = view; |
|||
HandleEvents = true; |
|||
_getPointFunc = getPointfunc; |
|||
_getInputRoot = getInputRoot; |
|||
} |
|||
|
|||
private TouchDevice _touchDevice = new TouchDevice(); |
|||
private Func<MotionEvent, int, Point> _getPointFunc; |
|||
private Func<IInputRoot> _getInputRoot; |
|||
|
|||
public bool? DispatchTouchEvent(MotionEvent e, out bool callBase) |
|||
{ |
|||
if (!HandleEvents) |
|||
{ |
|||
callBase = true; |
|||
return null; |
|||
} |
|||
|
|||
var eventTime = DateTime.Now; |
|||
|
|||
//Basic touch support
|
|||
var pointerEventType = e.Action switch |
|||
{ |
|||
MotionEventActions.Down => RawPointerEventType.TouchBegin, |
|||
MotionEventActions.Up => RawPointerEventType.TouchEnd, |
|||
MotionEventActions.Cancel => RawPointerEventType.TouchCancel, |
|||
_ => RawPointerEventType.TouchUpdate |
|||
}; |
|||
|
|||
if (e.Action.HasFlag(MotionEventActions.PointerDown)) |
|||
{ |
|||
pointerEventType = RawPointerEventType.TouchBegin; |
|||
} |
|||
|
|||
if (e.Action.HasFlag(MotionEventActions.PointerUp)) |
|||
{ |
|||
pointerEventType = RawPointerEventType.TouchEnd; |
|||
} |
|||
|
|||
for (int i = 0; i < e.PointerCount; i++) |
|||
{ |
|||
//if point is in view otherwise it's possible avalonia not to find the proper window to dispatch the event
|
|||
var point = _getPointFunc(e, i); |
|||
|
|||
double x = _view.View.GetX(); |
|||
double y = _view.View.GetY(); |
|||
double r = x + _view.View.Width; |
|||
double b = y + _view.View.Height; |
|||
|
|||
if (x <= point.X && r >= point.X && y <= point.Y && b >= point.Y) |
|||
{ |
|||
var inputRoot = _getInputRoot(); |
|||
|
|||
var mouseEvent = new RawTouchEventArgs(_touchDevice, (uint)eventTime.Ticks, inputRoot, |
|||
i == e.ActionIndex ? pointerEventType : RawPointerEventType.TouchUpdate, point, RawInputModifiers.None, e.GetPointerId(i)); |
|||
_view.Input(mouseEvent); |
|||
} |
|||
} |
|||
|
|||
callBase = true; |
|||
//if return false events for move and up are not received!!!
|
|||
return e.Action != MotionEventActions.Up; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
HandleEvents = false; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Generator; |
|||
using Microsoft.CodeAnalysis; |
|||
using Microsoft.CodeAnalysis.CSharp; |
|||
using Microsoft.CodeAnalysis.CSharp.Syntax; |
|||
|
|||
namespace DevGenerators; |
|||
|
|||
[Generator(LanguageNames.CSharp)] |
|||
public class EnumMemberDictionaryGenerator : IIncrementalGenerator |
|||
{ |
|||
const string DictionaryAttributeFullName = "global::Avalonia.SourceGenerator.GenerateEnumValueDictionaryAttribute"; |
|||
const string ListAttributeFullName = "global::Avalonia.SourceGenerator.GenerateEnumValueListAttribute"; |
|||
|
|||
public void Initialize(IncrementalGeneratorInitializationContext context) |
|||
{ |
|||
var allMethodsWithAttributes = context.SyntaxProvider |
|||
.CreateSyntaxProvider( |
|||
static (s, _) => s is MethodDeclarationSyntax |
|||
{ |
|||
AttributeLists.Count: > 0, |
|||
} md && md.Modifiers.Any(m=>m.IsKind(SyntaxKind.PartialKeyword)), |
|||
static (context, _) => |
|||
(IMethodSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!); |
|||
|
|||
var all = allMethodsWithAttributes |
|||
.Where(s => |
|||
s.HasAttributeWithFullyQualifiedName(DictionaryAttributeFullName) |
|||
|| s.HasAttributeWithFullyQualifiedName(ListAttributeFullName) |
|||
).Collect(); |
|||
context.RegisterSourceOutput(all, static (context, methods) => |
|||
{ |
|||
foreach (var typeGroup in methods.GroupBy(f => f.ContainingType)) |
|||
{ |
|||
var classBuilder = new StringBuilder(); |
|||
if (typeGroup.Key.ContainingNamespace != null) |
|||
classBuilder |
|||
.AppendLine("using System;") |
|||
.Append("namespace ") |
|||
.Append(typeGroup.Key.ContainingNamespace) |
|||
.AppendLine(";"); |
|||
classBuilder |
|||
.Append("partial class ") |
|||
.AppendLine(typeGroup.Key.Name) |
|||
.AppendLine("{"); |
|||
|
|||
foreach (var method in typeGroup) |
|||
{ |
|||
var namedReturn = method.ReturnType as INamedTypeSymbol; |
|||
var arrayReturn = method.ReturnType as IArrayTypeSymbol; |
|||
|
|||
if ((namedReturn != null && namedReturn.Arity > 0) || arrayReturn != null) |
|||
{ |
|||
ITypeSymbol enumType = namedReturn != null |
|||
? namedReturn.TypeArguments.Last() |
|||
: arrayReturn!.ElementType; |
|||
|
|||
var isDic = method.HasAttributeWithFullyQualifiedName(DictionaryAttributeFullName); |
|||
|
|||
classBuilder |
|||
.Pad(1) |
|||
.Append("private static partial " + method.ReturnType + " " + method.Name + "()") |
|||
.AppendLine().Pad(4).Append(" => new ").Append(method.ReturnType).AppendLine("{"); |
|||
foreach (var member in enumType.GetMembers()) |
|||
{ |
|||
if (member.Name == ".ctor") |
|||
continue; |
|||
|
|||
if (isDic) |
|||
classBuilder.Pad(2) |
|||
.Append("{\"") |
|||
.Append(member.Name) |
|||
.Append("\", ") |
|||
.Append(member.ToString()) |
|||
.AppendLine("},"); |
|||
else |
|||
classBuilder.Pad(2).Append(member.ToString()).AppendLine(","); |
|||
} |
|||
|
|||
classBuilder.Pad(1).AppendLine("};"); |
|||
} |
|||
} |
|||
classBuilder.AppendLine("}"); |
|||
|
|||
context.AddSource(typeGroup.Key.GetFullyQualifiedName().Replace(":", ""), classBuilder.ToString()); |
|||
} |
|||
}); |
|||
|
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Generator; |
|||
using Microsoft.CodeAnalysis; |
|||
using Microsoft.CodeAnalysis.CSharp; |
|||
using Microsoft.CodeAnalysis.CSharp.Syntax; |
|||
|
|||
namespace DevGenerators; |
|||
|
|||
[Generator(LanguageNames.CSharp)] |
|||
public class X11AtomsGenerator : IIncrementalGenerator |
|||
{ |
|||
public void Initialize(IncrementalGeneratorInitializationContext context) |
|||
{ |
|||
var x11AtomsClasses = context.SyntaxProvider |
|||
.CreateSyntaxProvider( |
|||
static (s, _) => s is ClassDeclarationSyntax |
|||
{ |
|||
Identifier.Text: "X11Atoms" |
|||
}, |
|||
static (context, _) => |
|||
(INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!); |
|||
|
|||
var all = x11AtomsClasses.Collect(); |
|||
context.RegisterSourceOutput(all, static (context, classes) => |
|||
{ |
|||
foreach (var cl in classes) |
|||
{ |
|||
var classBuilder = new StringBuilder(); |
|||
if (cl.ContainingNamespace != null) |
|||
classBuilder |
|||
.AppendLine("using System;") |
|||
.AppendLine("using static Avalonia.X11.XLib;") |
|||
.Append("namespace ") |
|||
.Append(cl.ContainingNamespace) |
|||
.AppendLine(";"); |
|||
classBuilder |
|||
.Append("partial class ") |
|||
.AppendLine(cl.Name) |
|||
.AppendLine("{"); |
|||
|
|||
var fields = cl.GetMembers().OfType<IFieldSymbol>() |
|||
.Where(f => f.Type.Name == "IntPtr" |
|||
&& f.DeclaredAccessibility == Accessibility.Public).ToList(); |
|||
|
|||
classBuilder.Pad(1).AppendLine("private void PopulateAtoms(IntPtr display)").Pad(1).AppendLine("{"); |
|||
classBuilder.Pad(2).Append("var atoms = new IntPtr[").Append(fields.Count).AppendLine("];"); |
|||
classBuilder.Pad(2).Append("var atomNames = new string[").Append(fields.Count).AppendLine("] {"); |
|||
|
|||
|
|||
for (int c = 0; c < fields.Count; c++) |
|||
classBuilder.Pad(3).Append("\"").Append(fields[c].Name).AppendLine("\","); |
|||
classBuilder.Pad(2).AppendLine("};"); |
|||
|
|||
classBuilder.Pad(2).AppendLine("XInternAtoms(display, atomNames, atomNames.Length, true, atoms);"); |
|||
|
|||
for (int c = 0; c < fields.Count; c++) |
|||
classBuilder.Pad(2).Append("InitAtom(ref ").Append(fields[c].Name).Append(", \"") |
|||
.Append(fields[c].Name).Append("\", atoms[").Append(c).AppendLine("]);"); |
|||
|
|||
|
|||
classBuilder.Pad(1).AppendLine("}"); |
|||
classBuilder.AppendLine("}"); |
|||
|
|||
context.AddSource(cl.GetFullyQualifiedName().Replace(":", ""), classBuilder.ToString()); |
|||
} |
|||
}); |
|||
|
|||
|
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue