Added initial touch support to the Forms views:
- iOS - Android - macOS
This commit is contained in:
Родитель
fda999a93b
Коммит
735a3d2c0a
|
@ -0,0 +1,91 @@
|
|||
using System;
|
||||
using Android.Views;
|
||||
|
||||
namespace SkiaSharp.Views.Forms
|
||||
{
|
||||
internal class SKTouchHandler
|
||||
{
|
||||
private Action<SKTouchActionEventArgs> onTouchAction;
|
||||
private Func<float, float> scalePixels;
|
||||
|
||||
public SKTouchHandler(Action<SKTouchActionEventArgs> onTouchAction, Func<float, float> scalePixels)
|
||||
{
|
||||
this.onTouchAction = onTouchAction;
|
||||
this.scalePixels = scalePixels;
|
||||
}
|
||||
|
||||
public void Attach(View view)
|
||||
{
|
||||
view.Touch += OnTouch;
|
||||
}
|
||||
|
||||
public void Detach(View view)
|
||||
{
|
||||
// clean the view
|
||||
if (view != null)
|
||||
{
|
||||
view.Touch -= OnTouch;
|
||||
}
|
||||
|
||||
// remove references
|
||||
onTouchAction = null;
|
||||
scalePixels = null;
|
||||
}
|
||||
|
||||
private void OnTouch(object sender, View.TouchEventArgs e)
|
||||
{
|
||||
if (onTouchAction == null || scalePixels == null)
|
||||
return;
|
||||
|
||||
var evt = e.Event;
|
||||
var pointer = evt.ActionIndex;
|
||||
|
||||
var id = evt.GetPointerId(pointer);
|
||||
var coords = new SKPoint(scalePixels(evt.GetX(pointer)), scalePixels(evt.GetY(pointer)));
|
||||
|
||||
switch (evt.ActionMasked)
|
||||
{
|
||||
case MotionEventActions.Down:
|
||||
case MotionEventActions.PointerDown:
|
||||
{
|
||||
var args = new SKTouchActionEventArgs(id, SKTouchActionType.Pressed, coords);
|
||||
onTouchAction(args);
|
||||
e.Handled = args.Handled;
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEventActions.Move:
|
||||
{
|
||||
var count = evt.PointerCount;
|
||||
for (pointer = 0; pointer < count; pointer++)
|
||||
{
|
||||
id = evt.GetPointerId(pointer);
|
||||
coords = new SKPoint(scalePixels(evt.GetX(pointer)), scalePixels(evt.GetY(pointer)));
|
||||
|
||||
var args = new SKTouchActionEventArgs(id, SKTouchActionType.Moved, coords);
|
||||
onTouchAction(args);
|
||||
e.Handled = e.Handled || args.Handled;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEventActions.Up:
|
||||
case MotionEventActions.PointerUp:
|
||||
{
|
||||
var args = new SKTouchActionEventArgs(id, SKTouchActionType.Released, coords);
|
||||
onTouchAction(args);
|
||||
e.Handled = args.Handled;
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEventActions.Cancel:
|
||||
{
|
||||
var args = new SKTouchActionEventArgs(id, SKTouchActionType.Cancelled, coords);
|
||||
onTouchAction(args);
|
||||
e.Handled = args.Handled;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -85,6 +85,7 @@
|
|||
<Compile Include="SKCanvasViewRenderer.cs" />
|
||||
<Compile Include="SKGLViewRenderer.cs" />
|
||||
<Compile Include="SKImageSourceHandler.cs" />
|
||||
<Compile Include="SKTouchHandler.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Foundation;
|
||||
using AppKit;
|
||||
|
||||
namespace SkiaSharp.Views.Forms
|
||||
{
|
||||
internal class SKTouchHandler : NSGestureRecognizer
|
||||
{
|
||||
private Action<SKTouchActionEventArgs> onTouchAction;
|
||||
private Func<nfloat, nfloat> scalePixels;
|
||||
|
||||
public SKTouchHandler(Action<SKTouchActionEventArgs> onTouchAction, Func<nfloat, nfloat> scalePixels)
|
||||
{
|
||||
this.onTouchAction = onTouchAction;
|
||||
this.scalePixels = scalePixels;
|
||||
}
|
||||
|
||||
public void Attach(NSView view)
|
||||
{
|
||||
view.AddGestureRecognizer(this);
|
||||
}
|
||||
|
||||
public void Detach(NSView view)
|
||||
{
|
||||
// clean the view
|
||||
if (view != null)
|
||||
{
|
||||
view.RemoveGestureRecognizer(this);
|
||||
}
|
||||
|
||||
// remove references
|
||||
onTouchAction = null;
|
||||
scalePixels = null;
|
||||
}
|
||||
|
||||
public override void MouseDown(NSEvent mouseEvent)
|
||||
{
|
||||
base.MouseDown(mouseEvent);
|
||||
|
||||
FireEvent(SKTouchActionType.Pressed, mouseEvent);
|
||||
}
|
||||
|
||||
public override void MouseUp(NSEvent mouseEvent)
|
||||
{
|
||||
base.MouseUp(mouseEvent);
|
||||
|
||||
FireEvent(SKTouchActionType.Released, mouseEvent);
|
||||
}
|
||||
|
||||
public override void MouseDragged(NSEvent mouseEvent)
|
||||
{
|
||||
base.MouseDragged(mouseEvent);
|
||||
|
||||
FireEvent(SKTouchActionType.Moved, mouseEvent);
|
||||
}
|
||||
|
||||
private bool FireEvent(SKTouchActionType actionType, NSEvent mouseEvent)
|
||||
{
|
||||
if (onTouchAction == null || scalePixels == null)
|
||||
return false;
|
||||
|
||||
var id = mouseEvent.ButtonNumber;
|
||||
|
||||
var cgPoint = LocationInView(View);
|
||||
// flip the Y coordinate for macOS
|
||||
cgPoint.Y = View.Bounds.Height - cgPoint.Y;
|
||||
|
||||
var point = new SKPoint((float)scalePixels(cgPoint.X), (float)scalePixels(cgPoint.Y));
|
||||
|
||||
var args = new SKTouchActionEventArgs(id, actionType, point);
|
||||
onTouchAction(args);
|
||||
return args.Handled;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -61,6 +61,7 @@
|
|||
<Compile Include="SKCanvasViewRenderer.cs" />
|
||||
<Compile Include="SKGLViewRenderer.cs" />
|
||||
<Compile Include="SKImageSourceHandler.cs" />
|
||||
<Compile Include="SKTouchHandler.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Mac\SkiaSharp.Views.Mac.csproj">
|
||||
|
|
|
@ -27,6 +27,25 @@ namespace SkiaSharp.Views.Forms
|
|||
where TFormsView : SKFormsView
|
||||
where TNativeView : SKNativeView
|
||||
{
|
||||
private readonly SKTouchHandler touchHandler;
|
||||
|
||||
public SKCanvasViewRendererBase()
|
||||
{
|
||||
#if __ANDROID__
|
||||
touchHandler = new SKTouchHandler(
|
||||
args => ((ISKCanvasViewController)Element).OnTouchAction(args),
|
||||
coord => Element.IgnorePixelScaling ? (float)Context.FromPixels(coord) : coord);
|
||||
#elif __IOS__
|
||||
touchHandler = new SKTouchHandler(
|
||||
args => ((ISKCanvasViewController)Element).OnTouchAction(args),
|
||||
coord => Element.IgnorePixelScaling ? coord : coord * Control.ContentScaleFactor);
|
||||
#elif __MACOS__
|
||||
touchHandler = new SKTouchHandler(
|
||||
args => ((ISKCanvasViewController)Element).OnTouchAction(args),
|
||||
coord => Element.IgnorePixelScaling ? coord : coord * Control.Window.BackingScaleFactor);
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<TFormsView> e)
|
||||
{
|
||||
if (e.OldElement != null)
|
||||
|
@ -47,6 +66,7 @@ namespace SkiaSharp.Views.Forms
|
|||
{
|
||||
var view = CreateNativeControl();
|
||||
view.PaintSurface += OnPaintSurface;
|
||||
touchHandler.Attach(view);
|
||||
SetNativeControl(view);
|
||||
}
|
||||
|
||||
|
@ -102,6 +122,9 @@ namespace SkiaSharp.Views.Forms
|
|||
control.PaintSurface -= OnPaintSurface;
|
||||
}
|
||||
|
||||
// detach, regardless of state
|
||||
touchHandler.Detach(control);
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,15 @@ namespace SkiaSharp.Views.Forms
|
|||
where TFormsView : SKFormsView
|
||||
where TNativeView : SKNativeView
|
||||
{
|
||||
private readonly SKTouchHandler touchHandler;
|
||||
|
||||
public SKGLViewRendererBase()
|
||||
{
|
||||
touchHandler = new SKTouchHandler(
|
||||
args => ((ISKGLViewController)Element).OnTouchAction(args),
|
||||
coord => coord);
|
||||
}
|
||||
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<TFormsView> e)
|
||||
{
|
||||
if (e.OldElement != null)
|
||||
|
@ -52,6 +61,7 @@ namespace SkiaSharp.Views.Forms
|
|||
#else
|
||||
view.PaintSurface += OnPaintSurface;
|
||||
#endif
|
||||
touchHandler.Attach(view);
|
||||
SetNativeControl(view);
|
||||
}
|
||||
|
||||
|
@ -108,6 +118,9 @@ namespace SkiaSharp.Views.Forms
|
|||
#endif
|
||||
}
|
||||
|
||||
// detach, regardless of state
|
||||
touchHandler.Detach(control);
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@ namespace SkiaSharp.Views.Forms
|
|||
// the user can subscribe to repaint
|
||||
public event EventHandler<SKPaintSurfaceEventArgs> PaintSurface;
|
||||
|
||||
// the user can subscribe to touch events
|
||||
public event EventHandler<SKTouchActionEventArgs> TouchAction;
|
||||
|
||||
// the native listens to this event
|
||||
private event EventHandler SurfaceInvalidated;
|
||||
private event EventHandler<GetCanvasSizeEventArgs> GetCanvasSize;
|
||||
|
@ -47,6 +50,12 @@ namespace SkiaSharp.Views.Forms
|
|||
PaintSurface?.Invoke(this, e);
|
||||
}
|
||||
|
||||
// the native view responds to a touch
|
||||
protected virtual void OnTouchAction(SKTouchActionEventArgs e)
|
||||
{
|
||||
TouchAction?.Invoke(this, e);
|
||||
}
|
||||
|
||||
// ISKViewController implementation
|
||||
|
||||
event EventHandler ISKCanvasViewController.SurfaceInvalidated
|
||||
|
@ -66,6 +75,11 @@ namespace SkiaSharp.Views.Forms
|
|||
OnPaintSurface(e);
|
||||
}
|
||||
|
||||
void ISKCanvasViewController.OnTouchAction(SKTouchActionEventArgs e)
|
||||
{
|
||||
OnTouchAction(e);
|
||||
}
|
||||
|
||||
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
|
||||
{
|
||||
return new SizeRequest(new Size(40.0, 40.0));
|
||||
|
@ -80,5 +94,8 @@ namespace SkiaSharp.Views.Forms
|
|||
|
||||
// the native view tells the user to repaint
|
||||
void OnPaintSurface(SKPaintSurfaceEventArgs e);
|
||||
|
||||
// the native view responds to a touch
|
||||
void OnTouchAction(SKTouchActionEventArgs e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ namespace SkiaSharp.Views.Forms
|
|||
|
||||
// the user can subscribe to repaint
|
||||
public event EventHandler<SKPaintGLSurfaceEventArgs> PaintSurface;
|
||||
|
||||
// the user can subscribe to touch events
|
||||
public event EventHandler<SKTouchActionEventArgs> TouchAction;
|
||||
|
||||
// the native listens to this event
|
||||
private event EventHandler SurfaceInvalidated;
|
||||
|
@ -46,6 +49,12 @@ namespace SkiaSharp.Views.Forms
|
|||
{
|
||||
PaintSurface?.Invoke(this, e);
|
||||
}
|
||||
|
||||
// the native view responds to a touch
|
||||
protected virtual void OnTouchAction(SKTouchActionEventArgs e)
|
||||
{
|
||||
TouchAction?.Invoke(this, e);
|
||||
}
|
||||
|
||||
// ISKViewController implementation
|
||||
|
||||
|
@ -66,6 +75,11 @@ namespace SkiaSharp.Views.Forms
|
|||
OnPaintSurface(e);
|
||||
}
|
||||
|
||||
void ISKGLViewController.OnTouchAction(SKTouchActionEventArgs e)
|
||||
{
|
||||
OnTouchAction(e);
|
||||
}
|
||||
|
||||
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
|
||||
{
|
||||
return new SizeRequest(new Size(40.0, 40.0));
|
||||
|
@ -80,5 +94,8 @@ namespace SkiaSharp.Views.Forms
|
|||
|
||||
// the native view tells the user to repaint
|
||||
void OnPaintSurface(SKPaintGLSurfaceEventArgs e);
|
||||
|
||||
// the native view responds to a touch
|
||||
void OnTouchAction(SKTouchActionEventArgs e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace SkiaSharp.Views.Forms
|
||||
{
|
||||
public class SKTouchActionEventArgs : EventArgs
|
||||
{
|
||||
public SKTouchActionEventArgs(long id, SKTouchActionType type, SKPoint[] locations)
|
||||
{
|
||||
Id = id;
|
||||
Type = type;
|
||||
Locations = locations ?? new SKPoint[0];
|
||||
Handled = true;
|
||||
}
|
||||
|
||||
public SKTouchActionEventArgs(long id, SKTouchActionType type, SKPoint location)
|
||||
{
|
||||
Id = id;
|
||||
Type = type;
|
||||
Locations = new[] { location };
|
||||
Handled = true;
|
||||
}
|
||||
|
||||
// this may be removed, but for now keep it
|
||||
internal bool Handled { get; set; }
|
||||
|
||||
public long Id { get; private set; }
|
||||
|
||||
public SKTouchActionType Type { get; private set; }
|
||||
|
||||
public SKPoint[] Locations { get; private set; }
|
||||
|
||||
public SKPoint Location => Locations.FirstOrDefault();
|
||||
|
||||
public bool InContact => Type == SKTouchActionType.Pressed || Type == SKTouchActionType.Moved;
|
||||
}
|
||||
|
||||
public enum SKTouchActionType
|
||||
{
|
||||
Pressed,
|
||||
Moved,
|
||||
Released,
|
||||
Cancelled
|
||||
}
|
||||
}
|
|
@ -17,5 +17,6 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)SKCanvasView.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)RendererTypes.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)SKImageSource.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)SKTouchActionEventArgs.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -13,7 +13,6 @@ namespace SkiaSharp.Views.Forms
|
|||
{
|
||||
var view = base.CreateNativeControl();
|
||||
|
||||
view.UserInteractionEnabled = false;
|
||||
// Force the opacity to false for consistency with the other platforms
|
||||
view.Opaque = false;
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ namespace SkiaSharp.Views.Forms
|
|||
{
|
||||
var view = base.CreateNativeControl();
|
||||
|
||||
view.UserInteractionEnabled = false;
|
||||
// Force the opacity to false for consistency with the other platforms
|
||||
view.Opaque = false;
|
||||
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
|
||||
namespace SkiaSharp.Views.Forms
|
||||
{
|
||||
internal class SKTouchHandler : UIGestureRecognizer
|
||||
{
|
||||
private Action<SKTouchActionEventArgs> onTouchAction;
|
||||
private Func<nfloat, nfloat> scalePixels;
|
||||
|
||||
public SKTouchHandler(Action<SKTouchActionEventArgs> onTouchAction, Func<nfloat, nfloat> scalePixels)
|
||||
{
|
||||
this.onTouchAction = onTouchAction;
|
||||
this.scalePixels = scalePixels;
|
||||
}
|
||||
|
||||
public void Attach(UIView view)
|
||||
{
|
||||
view.AddGestureRecognizer(this);
|
||||
}
|
||||
|
||||
public void Detach(UIView view)
|
||||
{
|
||||
// clean the view
|
||||
if (view != null)
|
||||
{
|
||||
view.RemoveGestureRecognizer(this);
|
||||
}
|
||||
|
||||
// remove references
|
||||
onTouchAction = null;
|
||||
scalePixels = null;
|
||||
}
|
||||
|
||||
public override void TouchesBegan(NSSet touches, UIEvent evt)
|
||||
{
|
||||
base.TouchesBegan(touches, evt);
|
||||
|
||||
foreach (UITouch touch in touches.Cast<UITouch>())
|
||||
{
|
||||
FireEvent(SKTouchActionType.Pressed, touch);
|
||||
}
|
||||
}
|
||||
|
||||
public override void TouchesMoved(NSSet touches, UIEvent evt)
|
||||
{
|
||||
base.TouchesMoved(touches, evt);
|
||||
|
||||
foreach (UITouch touch in touches.Cast<UITouch>())
|
||||
{
|
||||
FireEvent(SKTouchActionType.Moved, touch);
|
||||
}
|
||||
}
|
||||
|
||||
public override void TouchesEnded(NSSet touches, UIEvent evt)
|
||||
{
|
||||
base.TouchesEnded(touches, evt);
|
||||
|
||||
foreach (UITouch touch in touches.Cast<UITouch>())
|
||||
{
|
||||
FireEvent(SKTouchActionType.Released, touch);
|
||||
}
|
||||
}
|
||||
|
||||
public override void TouchesCancelled(NSSet touches, UIEvent evt)
|
||||
{
|
||||
base.TouchesCancelled(touches, evt);
|
||||
|
||||
foreach (UITouch touch in touches.Cast<UITouch>())
|
||||
{
|
||||
FireEvent(SKTouchActionType.Cancelled, touch);
|
||||
}
|
||||
}
|
||||
|
||||
private bool FireEvent(SKTouchActionType actionType, UITouch touch)
|
||||
{
|
||||
if (onTouchAction == null || scalePixels == null)
|
||||
return false;
|
||||
|
||||
var id = touch.Handle.ToInt64();
|
||||
|
||||
var cgPoint = touch.LocationInView(View);
|
||||
var point = new SKPoint((float)scalePixels(cgPoint.X), (float)scalePixels(cgPoint.Y));
|
||||
|
||||
var args = new SKTouchActionEventArgs(id, actionType, point);
|
||||
onTouchAction(args);
|
||||
return args.Handled;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -69,6 +69,7 @@
|
|||
<Compile Include="SKGLViewRenderer.cs" />
|
||||
<Compile Include="SKCanvasViewRenderer.cs" />
|
||||
<Compile Include="SKImageSourceHandler.cs" />
|
||||
<Compile Include="SKTouchHandler.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Binding\SkiaSharp.iOS\SkiaSharp.iOS.csproj">
|
||||
|
|
Загрузка…
Ссылка в новой задаче