Added initial touch support to the Forms views:

- iOS
 - Android
 - macOS
This commit is contained in:
Matthew Leibowitz 2017-05-31 21:42:55 -05:00
Родитель fda999a93b
Коммит 735a3d2c0a
14 изменённых файлов: 378 добавлений и 2 удалений

Просмотреть файл

@ -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="SKCanvasViewRenderer.cs" />
<Compile Include="SKGLViewRenderer.cs" /> <Compile Include="SKGLViewRenderer.cs" />
<Compile Include="SKImageSourceHandler.cs" /> <Compile Include="SKImageSourceHandler.cs" />
<Compile Include="SKTouchHandler.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <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="SKCanvasViewRenderer.cs" />
<Compile Include="SKGLViewRenderer.cs" /> <Compile Include="SKGLViewRenderer.cs" />
<Compile Include="SKImageSourceHandler.cs" /> <Compile Include="SKImageSourceHandler.cs" />
<Compile Include="SKTouchHandler.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Mac\SkiaSharp.Views.Mac.csproj"> <ProjectReference Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Mac\SkiaSharp.Views.Mac.csproj">

Просмотреть файл

@ -27,6 +27,25 @@ namespace SkiaSharp.Views.Forms
where TFormsView : SKFormsView where TFormsView : SKFormsView
where TNativeView : SKNativeView 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) protected override void OnElementChanged(ElementChangedEventArgs<TFormsView> e)
{ {
if (e.OldElement != null) if (e.OldElement != null)
@ -47,6 +66,7 @@ namespace SkiaSharp.Views.Forms
{ {
var view = CreateNativeControl(); var view = CreateNativeControl();
view.PaintSurface += OnPaintSurface; view.PaintSurface += OnPaintSurface;
touchHandler.Attach(view);
SetNativeControl(view); SetNativeControl(view);
} }
@ -102,6 +122,9 @@ namespace SkiaSharp.Views.Forms
control.PaintSurface -= OnPaintSurface; control.PaintSurface -= OnPaintSurface;
} }
// detach, regardless of state
touchHandler.Detach(control);
base.Dispose(disposing); base.Dispose(disposing);
} }

Просмотреть файл

@ -28,6 +28,15 @@ namespace SkiaSharp.Views.Forms
where TFormsView : SKFormsView where TFormsView : SKFormsView
where TNativeView : SKNativeView 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) protected override void OnElementChanged(ElementChangedEventArgs<TFormsView> e)
{ {
if (e.OldElement != null) if (e.OldElement != null)
@ -52,6 +61,7 @@ namespace SkiaSharp.Views.Forms
#else #else
view.PaintSurface += OnPaintSurface; view.PaintSurface += OnPaintSurface;
#endif #endif
touchHandler.Attach(view);
SetNativeControl(view); SetNativeControl(view);
} }
@ -108,6 +118,9 @@ namespace SkiaSharp.Views.Forms
#endif #endif
} }
// detach, regardless of state
touchHandler.Detach(control);
base.Dispose(disposing); base.Dispose(disposing);
} }

Просмотреть файл

@ -12,6 +12,9 @@ namespace SkiaSharp.Views.Forms
// the user can subscribe to repaint // the user can subscribe to repaint
public event EventHandler<SKPaintSurfaceEventArgs> PaintSurface; public event EventHandler<SKPaintSurfaceEventArgs> PaintSurface;
// the user can subscribe to touch events
public event EventHandler<SKTouchActionEventArgs> TouchAction;
// the native listens to this event // the native listens to this event
private event EventHandler SurfaceInvalidated; private event EventHandler SurfaceInvalidated;
private event EventHandler<GetCanvasSizeEventArgs> GetCanvasSize; private event EventHandler<GetCanvasSizeEventArgs> GetCanvasSize;
@ -47,6 +50,12 @@ namespace SkiaSharp.Views.Forms
PaintSurface?.Invoke(this, e); PaintSurface?.Invoke(this, e);
} }
// the native view responds to a touch
protected virtual void OnTouchAction(SKTouchActionEventArgs e)
{
TouchAction?.Invoke(this, e);
}
// ISKViewController implementation // ISKViewController implementation
event EventHandler ISKCanvasViewController.SurfaceInvalidated event EventHandler ISKCanvasViewController.SurfaceInvalidated
@ -66,6 +75,11 @@ namespace SkiaSharp.Views.Forms
OnPaintSurface(e); OnPaintSurface(e);
} }
void ISKCanvasViewController.OnTouchAction(SKTouchActionEventArgs e)
{
OnTouchAction(e);
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{ {
return new SizeRequest(new Size(40.0, 40.0)); 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 // the native view tells the user to repaint
void OnPaintSurface(SKPaintSurfaceEventArgs e); 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 // the user can subscribe to repaint
public event EventHandler<SKPaintGLSurfaceEventArgs> PaintSurface; public event EventHandler<SKPaintGLSurfaceEventArgs> PaintSurface;
// the user can subscribe to touch events
public event EventHandler<SKTouchActionEventArgs> TouchAction;
// the native listens to this event // the native listens to this event
private event EventHandler SurfaceInvalidated; private event EventHandler SurfaceInvalidated;
@ -46,6 +49,12 @@ namespace SkiaSharp.Views.Forms
{ {
PaintSurface?.Invoke(this, e); PaintSurface?.Invoke(this, e);
} }
// the native view responds to a touch
protected virtual void OnTouchAction(SKTouchActionEventArgs e)
{
TouchAction?.Invoke(this, e);
}
// ISKViewController implementation // ISKViewController implementation
@ -66,6 +75,11 @@ namespace SkiaSharp.Views.Forms
OnPaintSurface(e); OnPaintSurface(e);
} }
void ISKGLViewController.OnTouchAction(SKTouchActionEventArgs e)
{
OnTouchAction(e);
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{ {
return new SizeRequest(new Size(40.0, 40.0)); 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 // the native view tells the user to repaint
void OnPaintSurface(SKPaintGLSurfaceEventArgs e); 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)SKCanvasView.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RendererTypes.cs" /> <Compile Include="$(MSBuildThisFileDirectory)RendererTypes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SKImageSource.cs" /> <Compile Include="$(MSBuildThisFileDirectory)SKImageSource.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SKTouchActionEventArgs.cs" />
</ItemGroup> </ItemGroup>
</Project> </Project>

Просмотреть файл

@ -13,7 +13,6 @@ namespace SkiaSharp.Views.Forms
{ {
var view = base.CreateNativeControl(); var view = base.CreateNativeControl();
view.UserInteractionEnabled = false;
// Force the opacity to false for consistency with the other platforms // Force the opacity to false for consistency with the other platforms
view.Opaque = false; view.Opaque = false;

Просмотреть файл

@ -17,7 +17,6 @@ namespace SkiaSharp.Views.Forms
{ {
var view = base.CreateNativeControl(); var view = base.CreateNativeControl();
view.UserInteractionEnabled = false;
// Force the opacity to false for consistency with the other platforms // Force the opacity to false for consistency with the other platforms
view.Opaque = false; 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="SKGLViewRenderer.cs" />
<Compile Include="SKCanvasViewRenderer.cs" /> <Compile Include="SKCanvasViewRenderer.cs" />
<Compile Include="SKImageSourceHandler.cs" /> <Compile Include="SKImageSourceHandler.cs" />
<Compile Include="SKTouchHandler.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\Binding\SkiaSharp.iOS\SkiaSharp.iOS.csproj"> <ProjectReference Include="..\..\..\Binding\SkiaSharp.iOS\SkiaSharp.iOS.csproj">