This commit is contained in:
Wouter Steenbergen 2021-08-23 11:14:13 -07:00
Родитель a896c01bcf
Коммит f527b2ad1e
24 изменённых файлов: 760 добавлений и 91 удалений

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

@ -1,4 +1,6 @@
using FluidSharp.Engine;
#if !__IOS__
using FluidSharp.Engine;
using FluidSharp.Interop;
using FluidSharp.State;
using FluidSharp.Widgets;
@ -231,3 +233,5 @@ namespace FluidSharp.Views.UWP
}
}
#endif

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

@ -3,6 +3,13 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<Version>1.0.3-alpha</Version>
<Authors>Wouter Steenbergen</Authors>
<Company>My Daily Bits LLC</Company>
<Product>FluidSharp</Product>
<Description>FluidSharp is a high performance mobile first multi-platform UI layout framework based on Skia.</Description>
<Copyright>2020 Wouter Steenbergen</Copyright>
<PackageLicenseFile>License.txt</PackageLicenseFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@ -15,6 +22,10 @@
<ItemGroup>
<None Remove="License.txt" />
<None Include="License.txt">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<ItemGroup>
@ -28,11 +39,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp.Views.WindowsForms" Version="2.80.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\FluidSharp\FluidSharp.csproj" />
<PackageReference Include="SkiaSharp.Views.WindowsForms" Version="2.80.3" />
<PackageReference Include="FluidSharp" Version="1.0.3-alpha" />
</ItemGroup>
<ItemGroup>

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

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<package >
<metadata>
<id>FluidSharp.Views.WindowsForms.Core</id>
<version>1.0.1-alpha</version>
<title>$title$</title>
<authors>Wouter Steenbergen</authors>
<owners>My Daily Bits LLC</owners>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<license type="file">License.txt</license>
<description>FluidSharp is a high performance mobile first multi-platform UI layout framework based on Skia.</description>
<releaseNotes>Initial release</releaseNotes>
<copyright>Copyright 2021 Wouter Steenbergen</copyright>
<dependencies>
<group targetFramework="netcoreapp3.1">
<dependency id="FluidSharp" version="1.0.1-alpha" />
</group>
</dependencies>
</metadata>
<files>
<!-- Cross-platform reference assemblies -->
<file src="License.txt"/>
<file src="bin\Release\netcoreapp3.1\FluidSharp.Views.WindowsForms.Core.dll" target="lib\netcoreapp3.1\FluidSharp.Views.WindowsForms.Core.dll" />
</files>
</package>

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

@ -39,6 +39,9 @@
<Compile Include="..\FluidSharp.Views.Shared\FluidWidgetView.cs">
<Link>FluidWidgetView.cs</Link>
</Compile>
<Compile Include="FluidUICanvasViewController.cs" />
<Compile Include="FluidWidgetGLView.cs" />
<Compile Include="FluidWidgetCanvasView.cs" />
<Compile Include="NativeViewManager.cs" />
<Compile Include="NativeViews\INativeTextboxImpl.cs" />
<Compile Include="NativeViews\NativeMultiLineTextboxImpl.cs">
@ -51,18 +54,22 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\KeyboardTracker.cs" />
<Compile Include="SkiaView.cs" />
<Compile Include="SkiaViews.cs" />
<Compile Include="TouchRecognizer.cs" />
<Compile Include="FluidUIViewController.cs" />
<Compile Include="FluidUIGLViewController.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp">
<Version>2.80.2</Version>
<Version>2.80.3</Version>
</PackageReference>
<PackageReference Include="SkiaSharp.HarfBuzz">
<Version>2.80.2</Version>
<Version>2.80.3</Version>
</PackageReference>
<PackageReference Include="SkiaSharp.Views">
<Version>2.80.2</Version>
<Version>2.80.3</Version>
</PackageReference>
<PackageReference Include="System.Drawing.Common">
<Version>5.0.2</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>

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

@ -2,7 +2,7 @@
<package >
<metadata>
<id>FluidSharp.Views.iOS</id>
<version>1.0.3-alpha</version>
<version>1.0.4-alpha</version>
<title>$title$</title>
<authors>Wouter Steenbergen</authors>
<owners>My Daily Bits LLC</owners>

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

@ -0,0 +1,86 @@
using CoreGraphics;
using FluidSharp.Layouts;
using FluidSharp.Views.iOS;
using FluidSharp.Views.iOS.Services;
using Foundation;
using System;
using System.Threading.Tasks;
using UIKit;
namespace FluidSharp.Views.iOS
{
public class FluidUICanvasViewController : UIViewController
{
public readonly IWidgetSource WidgetSource;
private FluidWidgetCanvasView FluidWidgetView;
private KeyboardTracker KeyboardTracker;
private nfloat OriginalInsetBottom;
private Action<Margins> OnDeviceMarginsChanged;
public FluidUICanvasViewController(IWidgetSource widgetSource, Action<Margins> onDeviceMarginsChanged)
{
WidgetSource = widgetSource;
OnDeviceMarginsChanged = onDeviceMarginsChanged;
KeyboardTracker = new KeyboardTracker(h =>
{
if (h == 0)
{
AdditionalSafeAreaInsets = new UIEdgeInsets(0, 0, 0, 0);
}
else
{
if (AdditionalSafeAreaInsets.Bottom == 0)
OriginalInsetBottom = View.SafeAreaInsets.Bottom;
AdditionalSafeAreaInsets = new UIEdgeInsets(0, 0, h - OriginalInsetBottom, 0);
}
View.LayoutIfNeeded();
});
}
public override void ViewDidLayoutSubviews() { if (FluidWidgetView != null) Task.Run(RequestRedraw); }
public Task RequestRedraw() => FluidWidgetView?.VisualState.RequestRedraw() ?? Task.CompletedTask;
public override void LoadView()
{
#if USEGL
FluidWidgetView = new FluidWidgetGLView();
#else
FluidWidgetView = new FluidWidgetCanvasView();
#endif
FluidWidgetView.WidgetSource = WidgetSource;
View = FluidWidgetView;
}
public override void ViewWillAppear(bool animated) => KeyboardTracker.RegisterForKeyboardNotifications();
public override void ViewWillDisappear(bool animated) => KeyboardTracker.UnregisterForKeyboardNotifications();
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
}
public override void DidReceiveMemoryWarning()
{
base.DidReceiveMemoryWarning();
// Release any cached data, images, etc that aren't in use.
}
public override void ViewSafeAreaInsetsDidChange()
{
var sai = View.SafeAreaInsets;
OnDeviceMarginsChanged(new Margins((float)sai.Left, (float)sai.Top, (float)sai.Right, (float)sai.Bottom));
base.ViewSafeAreaInsetsDidChange();
}
public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection)
{
base.TraitCollectionDidChange(previousTraitCollection);
// View.TraitCollection.UserInterfaceStyle == UIUserInterfaceStyle.Dark
}
}
}

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

@ -9,18 +9,18 @@ using UIKit;
namespace FluidSharp.Views.iOS
{
public class FluidUIViewController : UIViewController
public class FluidUIGLViewController : UIViewController
{
public readonly IWidgetSource WidgetSource;
private FluidWidgetView FluidWidgetView;
private FluidWidgetGLView FluidWidgetView;
private KeyboardTracker KeyboardTracker;
private nfloat OriginalInsetBottom;
private Action<Margins> OnDeviceMarginsChanged;
public FluidUIViewController(IWidgetSource widgetSource, Action<Margins> onDeviceMarginsChanged)
public FluidUIGLViewController(IWidgetSource widgetSource, Action<Margins> onDeviceMarginsChanged)
{
WidgetSource = widgetSource;
OnDeviceMarginsChanged = onDeviceMarginsChanged;
@ -46,7 +46,7 @@ namespace FluidSharp.Views.iOS
public override void LoadView()
{
FluidWidgetView = new FluidWidgetView();
FluidWidgetView = new FluidWidgetGLView();
FluidWidgetView.WidgetSource = WidgetSource;
View = FluidWidgetView;
}
@ -79,4 +79,4 @@ namespace FluidSharp.Views.iOS
// View.TraitCollection.UserInterfaceStyle == UIUserInterfaceStyle.Dark
}
}
}
}

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

@ -0,0 +1,234 @@
using FluidSharp.Engine;
using FluidSharp.Interop;
using FluidSharp.State;
using FluidSharp.Widgets;
using FluidSharp.Widgets.Native;
using SkiaSharp;
using System;
#if __FORMS__
using FluidSharp.Views.Forms.NativeViews;
namespace FluidSharp.Views.Forms
#elif __WINDOWSFORMS__
using FluidSharp.Views.WindowsForms.NativeViews;
using System.Windows.Forms;
namespace FluidSharp.Views.WindowsForms
#elif __ANDROID__
using FluidSharp.Views.Android.NativeViews;
using Android.App;
using Android.Views;
namespace FluidSharp.Views.Android
#elif __IOS__
using UIKit;
using FluidSharp.Views.iOS.NativeViews;
namespace FluidSharp.Views.iOS
#elif __UWP__
namespace FluidSharp.Views.UWP
#endif
{
#if __ANDROID__
public class FluidWidgetView : global::Android.Widget.RelativeLayout, IFluidWidgetView
#else
public class FluidWidgetCanvasView : SkiaCanvasView, IFluidWidgetView
#endif
{
/// <summary>
/// The source of widgets, either overwrite MakeWidget, or set the WidgetSource to implement a custom view
/// </summary>
public IWidgetSource WidgetSource { get => widgetSource; set { widgetSource = value; SkiaView.InvalidatePaint(); } }
private IWidgetSource widgetSource;
public Device Device;
private FluidWidgetViewImplementation implementation;
public FluidWidgetViewImplementation Implementation
{
get => implementation;
set
{
if (implementation != null) implementation.Dispose();
implementation = value;
}
}
public NativeViewManager NativeViewManager;
public VisualState VisualState => Implementation.VisualState;
/// <summary>
/// Set AutoSizeHeight to true if the view should be sized by the (painted) height of the widgets.
/// The default is false.
/// </summary>
public bool AutoSizeHeight { get; set; }
private float LastPaintWidth = -1;
private float LastHeightRequest = -1;
#if __ANDROID__
private SkiaView SkiaView;
public SKSize PlatformScale => SkiaView.PlatformScale;
public FluidWidgetView(global::Android.Content.Context context) : base(context)
{
SkiaView = new SkiaView(context);
var fillparams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent);
AddView(SkiaView, fillparams);
#else
private SkiaCanvasView SkiaView => this;
public FluidWidgetCanvasView()
{
#endif
Device = new Device();
#if __FORMS__
Device.FlowDirection = Xamarin.Forms.Device.FlowDirection == Xamarin.Forms.FlowDirection.RightToLeft ? SkiaSharp.TextBlocks.Enum.FlowDirection.RightToLeft : SkiaSharp.TextBlocks.Enum.FlowDirection.LeftToRight;
#endif
NativeViewManager = new NativeViewManager(this);
Implementation = new FluidWidgetViewImplementation(SkiaView, this, Device);
RegisterNativeViews();
}
#if __ANDROID__
public FluidWidgetView(global::Android.Content.Context context, bool CreatesOwnImplementation) : base(context)
#else
protected FluidWidgetCanvasView(bool CreatesOwnImplementation)
#endif
{
if (!CreatesOwnImplementation) throw new ArgumentOutOfRangeException(nameof(CreatesOwnImplementation));
}
protected void RegisterNativeViews()
{
#if __IOS__
NativeViewManager.RegisterNativeView<NativeTextboxWidget, UIView>(
(w, c) => ((INativeTextboxImpl)c).Context.Equals(w.Context),
(w) => w.Keyboard == Keyboard.MultiLine ? (UIView)
new NativeMultiLineTextboxImpl(VisualState.RequestRedraw) { Context = w.Context } :
new NativeSingleLineTextboxImpl(VisualState.RequestRedraw) { Context = w.Context }
#else
NativeViewManager.RegisterNativeView<NativeTextboxWidget, NativeTextboxImpl>(
(w, c) => c.Context.Equals(w.Context),
#if __ANDROID__
(w) => new NativeTextboxImpl(Context, VisualState.RequestRedraw) { Context = w.Context }
#else
(w) => new NativeTextboxImpl(VisualState.RequestRedraw) { Context = w.Context }
#endif
#endif
);
}
public INativeViewManager GetNativeViewManager() => NativeViewManager;
/// <summary>
/// Either overwrite this method or set WidgetSource to implement a custom view
/// </summary>
public virtual Widget MakeWidget(VisualState visualState)
{
if (WidgetSource == null)
return Rectangle.Fill(SKColors.Teal);
else
return WidgetSource.MakeWidget(visualState);
}
public void SetHeight(float height)
{
// Maximum height: screen / device size
#if __FORMS__
#elif __WINDOWSFORMS__
height = Math.Min(Height, Screen.FromControl(this).WorkingArea.Height);
#elif __ANDROID__
#elif __IOS__
#elif __UWP__
#endif
// Minimum height: 10
height = Math.Max(height, 10);
// Apply height
#if __FORMS__
if (this.Height < height - 5 || this.Height > height + 5) InvalidateMeasure();
#elif __WINDOWSFORMS__
this.Height = (int)height;
#elif __ANDROID__
#elif __IOS__
#elif __UWP__
#endif
}
public virtual SKColor GetBackgroundColor(VisualState visualState)
{
if (widgetSource is IBackgroundColorSource backgroundColorSource) return backgroundColorSource.GetBackgroundColor(visualState);
return default;
}
#if __FORMS__
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
LastPaintWidth = (float)Width;
}
protected override void InvalidateMeasure()
{
base.InvalidateMeasure();
InvalidatePaint();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (BindingContext == null)
Implementation.Dispose();
}
protected override Xamarin.Forms.SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
if (Width == LastPaintWidth && LastHeightRequest > -1)
return new Xamarin.Forms.SizeRequest(new Xamarin.Forms.Size(LastPaintWidth, LastHeightRequest));
var request = Implementation.Measure(new SKSize((float)widthConstraint, (float)heightConstraint));
System.Diagnostics.Debug.WriteLine($"LinkTileView Measured: {request} ({widthConstraint}, {heightConstraint}) ");
if (float.IsInfinity(request.Width) || float.IsInfinity(request.Height))
return base.OnMeasure(widthConstraint, heightConstraint);
return new Xamarin.Forms.SizeRequest(new Xamarin.Forms.Size(request.Width, request.Height));
}
#endif
#if __WINDOWSFORMS__
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
Implementation.Dispose();
}
#endif
#if __ANDROID__
public void AddOnMainThread(View childview)
{
((Activity)Context).RunOnUiThread(() => AddView(childview));
}
#endif
}
}

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

@ -0,0 +1,234 @@
using FluidSharp.Engine;
using FluidSharp.Interop;
using FluidSharp.State;
using FluidSharp.Widgets;
using FluidSharp.Widgets.Native;
using SkiaSharp;
using System;
#if __FORMS__
using FluidSharp.Views.Forms.NativeViews;
namespace FluidSharp.Views.Forms
#elif __WINDOWSFORMS__
using FluidSharp.Views.WindowsForms.NativeViews;
using System.Windows.Forms;
namespace FluidSharp.Views.WindowsForms
#elif __ANDROID__
using FluidSharp.Views.Android.NativeViews;
using Android.App;
using Android.Views;
namespace FluidSharp.Views.Android
#elif __IOS__
using UIKit;
using FluidSharp.Views.iOS.NativeViews;
namespace FluidSharp.Views.iOS
#elif __UWP__
namespace FluidSharp.Views.UWP
#endif
{
#if __ANDROID__
public class FluidWidgetView : global::Android.Widget.RelativeLayout, IFluidWidgetView
#else
public class FluidWidgetGLView : SkiaGLView, IFluidWidgetView
#endif
{
/// <summary>
/// The source of widgets, either overwrite MakeWidget, or set the WidgetSource to implement a custom view
/// </summary>
public IWidgetSource WidgetSource { get => widgetSource; set { widgetSource = value; SkiaView.InvalidatePaint(); } }
private IWidgetSource widgetSource;
public Device Device;
private FluidWidgetViewImplementation implementation;
public FluidWidgetViewImplementation Implementation
{
get => implementation;
set
{
if (implementation != null) implementation.Dispose();
implementation = value;
}
}
public NativeViewManager NativeViewManager;
public VisualState VisualState => Implementation.VisualState;
/// <summary>
/// Set AutoSizeHeight to true if the view should be sized by the (painted) height of the widgets.
/// The default is false.
/// </summary>
public bool AutoSizeHeight { get; set; }
private float LastPaintWidth = -1;
private float LastHeightRequest = -1;
#if __ANDROID__
private SkiaView SkiaView;
public SKSize PlatformScale => SkiaView.PlatformScale;
public FluidWidgetView(global::Android.Content.Context context) : base(context)
{
SkiaView = new SkiaView(context);
var fillparams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent);
AddView(SkiaView, fillparams);
#else
private SkiaGLView SkiaView => this;
public FluidWidgetGLView()
{
#endif
Device = new Device();
#if __FORMS__
Device.FlowDirection = Xamarin.Forms.Device.FlowDirection == Xamarin.Forms.FlowDirection.RightToLeft ? SkiaSharp.TextBlocks.Enum.FlowDirection.RightToLeft : SkiaSharp.TextBlocks.Enum.FlowDirection.LeftToRight;
#endif
NativeViewManager = new NativeViewManager(this);
Implementation = new FluidWidgetViewImplementation(SkiaView, this, Device);
RegisterNativeViews();
}
#if __ANDROID__
public FluidWidgetView(global::Android.Content.Context context, bool CreatesOwnImplementation) : base(context)
#else
protected FluidWidgetGLView(bool CreatesOwnImplementation)
#endif
{
if (!CreatesOwnImplementation) throw new ArgumentOutOfRangeException(nameof(CreatesOwnImplementation));
}
protected void RegisterNativeViews()
{
#if __IOS__
NativeViewManager.RegisterNativeView<NativeTextboxWidget, UIView>(
(w, c) => ((INativeTextboxImpl)c).Context.Equals(w.Context),
(w) => w.Keyboard == Keyboard.MultiLine ? (UIView)
new NativeMultiLineTextboxImpl(VisualState.RequestRedraw) { Context = w.Context } :
new NativeSingleLineTextboxImpl(VisualState.RequestRedraw) { Context = w.Context }
#else
NativeViewManager.RegisterNativeView<NativeTextboxWidget, NativeTextboxImpl>(
(w, c) => c.Context.Equals(w.Context),
#if __ANDROID__
(w) => new NativeTextboxImpl(Context, VisualState.RequestRedraw) { Context = w.Context }
#else
(w) => new NativeTextboxImpl(VisualState.RequestRedraw) { Context = w.Context }
#endif
#endif
);
}
public INativeViewManager GetNativeViewManager() => NativeViewManager;
/// <summary>
/// Either overwrite this method or set WidgetSource to implement a custom view
/// </summary>
public virtual Widget MakeWidget(VisualState visualState)
{
if (WidgetSource == null)
return Rectangle.Fill(SKColors.Teal);
else
return WidgetSource.MakeWidget(visualState);
}
public void SetHeight(float height)
{
// Maximum height: screen / device size
#if __FORMS__
#elif __WINDOWSFORMS__
height = Math.Min(Height, Screen.FromControl(this).WorkingArea.Height);
#elif __ANDROID__
#elif __IOS__
#elif __UWP__
#endif
// Minimum height: 10
height = Math.Max(height, 10);
// Apply height
#if __FORMS__
if (this.Height < height - 5 || this.Height > height + 5) InvalidateMeasure();
#elif __WINDOWSFORMS__
this.Height = (int)height;
#elif __ANDROID__
#elif __IOS__
#elif __UWP__
#endif
}
public virtual SKColor GetBackgroundColor(VisualState visualState)
{
if (widgetSource is IBackgroundColorSource backgroundColorSource) return backgroundColorSource.GetBackgroundColor(visualState);
return default;
}
#if __FORMS__
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
LastPaintWidth = (float)Width;
}
protected override void InvalidateMeasure()
{
base.InvalidateMeasure();
InvalidatePaint();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (BindingContext == null)
Implementation.Dispose();
}
protected override Xamarin.Forms.SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
if (Width == LastPaintWidth && LastHeightRequest > -1)
return new Xamarin.Forms.SizeRequest(new Xamarin.Forms.Size(LastPaintWidth, LastHeightRequest));
var request = Implementation.Measure(new SKSize((float)widthConstraint, (float)heightConstraint));
System.Diagnostics.Debug.WriteLine($"LinkTileView Measured: {request} ({widthConstraint}, {heightConstraint}) ");
if (float.IsInfinity(request.Width) || float.IsInfinity(request.Height))
return base.OnMeasure(widthConstraint, heightConstraint);
return new Xamarin.Forms.SizeRequest(new Xamarin.Forms.Size(request.Width, request.Height));
}
#endif
#if __WINDOWSFORMS__
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
Implementation.Dispose();
}
#endif
#if __ANDROID__
public void AddOnMainThread(View childview)
{
((Activity)Context).RunOnUiThread(() => AddView(childview));
}
#endif
}
}

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

@ -1,4 +1,5 @@
#define USEGL
#if false
#define USEGL
using FluidSharp.Touch;
using SkiaSharp;
using SkiaSharp.Views.iOS;
@ -97,3 +98,4 @@ namespace FluidSharp.Views.iOS
}
}
#endif

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

@ -0,0 +1,129 @@
using FluidSharp.Touch;
using SkiaSharp;
using SkiaSharp.Views.iOS;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UIKit;
namespace FluidSharp.Views.iOS
{
public class SkiaGLView : SKGLView, ISkiaView
{
public float Width => (float)Bounds.Width;
public float Height => (float)Bounds.Height;
public event EventHandler<PaintSurfaceEventArgs> PaintViewSurface;
public event EventHandler<TouchActionEventArgs> Touch;
public void InvalidatePaint()
{
InvokeOnMainThread(() =>
{
SetNeedsDisplay();
});
}
public SkiaGLView()
{
var touchrecognizer = new TouchRecognizer(this);
GestureRecognizers = new UIGestureRecognizer[] { touchrecognizer };
touchrecognizer.Touch += Touchrecognizer_Touch;
}
protected override void OnPaintSurface(SKPaintGLSurfaceEventArgs e)
{
var canvas = e.Surface.Canvas;
// Make sure the canvas is drawn using pixel coordinates (but still high res):
var factor = (float)Math.Round(e.BackendRenderTarget.Width / Width * 4) / 4;
var platformzoom = SKMatrix.CreateScale(factor, factor);
canvas.Concat(ref platformzoom);
PaintViewSurface?.Invoke(this, new PaintSurfaceEventArgs(canvas, Width, Height, e.Surface, default));
}
private void Touchrecognizer_Touch(object sender, TouchActionEventArgs e)
{
Touch?.Invoke(this, e);
}
}
public class SkiaCanvasView : SKCanvasView, ISkiaView
{
public float Width => (float)Bounds.Width;
public float Height => (float)Bounds.Height;
public event EventHandler<PaintSurfaceEventArgs> PaintViewSurface;
public event EventHandler<TouchActionEventArgs> Touch;
public void InvalidatePaint()
{
InvokeOnMainThread(() =>
{
SetNeedsDisplay();
});
}
public SkiaCanvasView()
{
var touchrecognizer = new TouchRecognizer(this);
GestureRecognizers = new UIGestureRecognizer[] { touchrecognizer };
touchrecognizer.Touch += Touchrecognizer_Touch;
PaintSurface += SkiaControl_PaintSurface;
}
private void SkiaControl_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
var canvas = e.Surface.Canvas;
// Make sure the canvas is drawn using pixel coordinates (but still high res):
var factor = (float)Math.Round(e.Info.Width / Width * 4) / 4;
var platformzoom = SKMatrix.CreateScale(factor, factor);
canvas.Concat(ref platformzoom);
PaintViewSurface?.Invoke(this, new PaintSurfaceEventArgs(canvas, Width, Height, e.Surface, default));
}
private void Touchrecognizer_Touch(object sender, TouchActionEventArgs e)
{
Touch?.Invoke(this, e);
}
//protected override void Dispose(bool disposing)
//{
// // detach all events before disposing
// var controller = (ISKCanvasViewController)Element;
// if (controller != null)
// {
// controller.SurfaceInvalidated -= OnSurfaceInvalidated;
// controller.GetCanvasSize -= OnGetCanvasSize;
// }
// var control = Control;
// if (control != null)
// {
// control.PaintSurface -= OnPaintSurface;
// }
// // detach, regardless of state
// touchHandler.Detach(control);
// base.Dispose(disposing);
//}
}
}

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

@ -8,16 +8,16 @@
<Description>FluidSharp is a high performance mobile first multi-platform UI layout framework based on Skia.</Description>
<Copyright>2020 Wouter Steenbergen</Copyright>
<PackageLicenseExpression></PackageLicenseExpression>
<Version>1.0.3-alpha</Version>
<Version>1.0.5-alpha</Version>
<Nullable>enable</Nullable>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseFile>License.txt</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.80.2" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.2" />
<PackageReference Include="Svg.Skia" Version="0.5.0" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.3" />
<PackageReference Include="Svg.Skia" Version="0.5.6.2" />
</ItemGroup>
<ItemGroup>

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

@ -92,34 +92,31 @@ namespace FluidSharp
public void DebugRect(SKRect rect, SKColor color)
{
if (Canvas == null) return;
#if DEBUG
using (var borderpaint = new SKPaint() { Color = color, IsStroke = true })
Canvas.DrawRect(rect, borderpaint);
#endif
}
public void DebugGestureRect(SKRect rect, SKColor color)
{
if (Canvas == null) return;
#if DEBUG
if (VisualState.ShowTouchRegions)
using (var paint = new SKPaint() { Color = color })
Canvas.DrawRect(rect, paint);
#endif
}
public void DebugLine(float x1, float y1, float x2, float y2, SKColor color)
{
if (Canvas == null) return;
#if DEBUG
using (var linepaint = new SKPaint() { Color = color, IsStroke = true })
Canvas.DrawLine(x1, y1, x2, y2, linepaint);
#endif
}
public void DebugMargin(SKRect inner, Margins margins, SKColor color)
{
#if DEBUG
var top = new SKRect(inner.Left, inner.Top - margins.Top, inner.Right, inner.Top);
DebugSpacing(top, margins.Top.ToString(), color);
@ -134,15 +131,12 @@ namespace FluidSharp
var far = inner.HorizontalAlign(new SKSize(-margins.Far, inner.Height), HorizontalAlignment.Far, FlowDirection);
DebugSpacing(far, margins.Far.ToString(), color);
#endif
}
public void DebugSpacing(SKRect dest, string text, SKColor color)
{
if (Canvas == null) return;
#if DEBUG
if (!VisualState.ShowSpacing) return;
@ -156,7 +150,6 @@ namespace FluidSharp
using (var textpaint = new SKPaint() { Color = color, IsAntialias = true })
Canvas.DrawText(text, dest.MidX - 4, dest.Bottom - 2, textpaint);
#endif
}

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

@ -26,7 +26,7 @@ namespace FluidSharp.Navigation
TransitionState.TransitionDuration = TimeSpan.FromSeconds(1);
}
public Widget MakeWidget(NavigationContainer navigationContainer, VisualState visualState, IWidgetSource from, IWidgetSource to, Func<Task> dismiss)
public Widget MakeWidget(NavigationContext context, VisualState visualState, IWidgetSource from, IWidgetSource to, Func<Task> dismiss)
{
var frame = TransitionState.GetFrame();

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

@ -34,7 +34,7 @@ namespace FluidSharp.Navigation
public Task OnTransitionCompleted(bool open) => !open ? OnTransitionCompleted2(open) : Task.CompletedTask;
public Widget MakeWidget(NavigationContainer navigationContainer, VisualState visualState, IWidgetSource from, IWidgetSource to, Func<Task> dismiss)
public Widget MakeWidget(NavigationContext context, VisualState visualState, IWidgetSource from, IWidgetSource to, Func<Task> dismiss)
{
var animation = TransitionState.GetAnimation(Easing.CubicOut);

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

@ -75,7 +75,7 @@ namespace FluidSharp.Touch
Location = new SKPoint(Location.X / fx, Location.Y / fy);
Scale = new SKPoint(Scale.X * fx, Scale.Y * fy);
if (ClipPathStack != null) throw new Exception("Scaling Clip Paths not supported");
if (ClipPathStack != null && ClipPathStack.Count > 0) throw new Exception("Scaling Clip Paths not supported");
ClipRectStack = new Stack<SKRect>();
foreach (var cliprect in originalClipRects.Reverse())

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

@ -1,6 +1,4 @@
#if DEBUG
#define DEBUGCONTAINER
#endif
#define SHOWSPACING
using FluidSharp.Layouts;
using SkiaSharp;
using System;
@ -39,7 +37,7 @@ namespace FluidSharp.Widgets
childrect = new SKRect(xm - childsize.Width / 2, childrect.Top, xm + childsize.Width / 2, childrect.Top + childsize.Height);
layoutsurface.Paint(Child, childrect);
#if DEBUGCONTAINER
#if SHOWSPACING
layoutsurface.DebugMargin(childrect, Margin, SKColors.YellowGreen);
#endif
return rect.WithHeight(childsize.Height + Margin.TotalY);

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

@ -1,6 +1,6 @@
#if DEBUG
#define SHOWSPACING
#if DEBUG
//#define DEBUGCONTAINER
#define SHOWSPACING
#endif
using FluidSharp.Layouts;
using SkiaSharp;

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

@ -1,6 +1,4 @@
#if DEBUG
#define DEBUGCONTAINER
#endif
#define SHOWSPACING
using FluidSharp.Layouts;
using SkiaSharp;
@ -151,7 +149,7 @@ namespace FluidSharp.Widgets
if (child != null)
layoutsurface.Paint(child, childrect);
#if DEBUGCONTAINER
#if SHOWSPACING
layoutsurface.DebugMargin(childrect, Margin, SKColors.YellowGreen);
//layoutsurface.DebugRect(drawrect, SKColors.Blue.WithAlpha(128));
#endif

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

@ -1,6 +1,4 @@
#if DEBUG
#define DEBUGCONTAINER
#endif
#define SHOWSPACING
using FluidSharp.Layouts;
using SkiaSharp;
using SkiaSharp.TextBlocks.Enum;
@ -90,7 +88,7 @@ namespace FluidSharp.Widgets
result = new SKRect(rect.Right - maxlinewidth, rect.Top, rect.Right, y);
#if DEBUGCONTAINER
#if SHOWSPACING
if (layoutsurface != null)
layoutsurface.DebugMargin(result, Margin, SKColors.YellowGreen);
#endif

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

@ -1,6 +1,4 @@
#if DEBUG
#define DEBUGCONTAINER
#endif
#define SHOWSPACING
using FluidSharp.Layouts;
using SkiaSharp;
using SkiaSharp.TextBlocks.Enum;
@ -243,7 +241,7 @@ namespace FluidSharp.Widgets
}
#if DEBUGCONTAINER
#if SHOWSPACING
if (Debug)
{

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

@ -1,6 +1,4 @@
#if DEBUG
#define DEBUGCONTAINER
#endif
#define SHOWSPACING
using FluidSharp.Layouts;
using SkiaSharp;
using System;
@ -71,7 +69,7 @@ namespace FluidSharp.Widgets
var farrect = childrect.HorizontalAlign(new SKSize(farwidth, childrect.Height), HorizontalAlignment.Far, layoutsurface.FlowDirection);
layoutsurface.Paint(Far, farrect);
#if DEBUGCONTAINER
#if SHOWSPACING
layoutsurface.DebugMargin(childrect, Margin, SKColors.YellowGreen);
//layoutsurface.DebugRect(drawrect, SKColors.Blue.WithAlpha(128));
#endif

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

@ -1,6 +1,4 @@
#if DEBUG
#define DEBUGCONTAINER
#endif
#define SHOWSPACING
using FluidSharp.Layouts;
using SkiaSharp;
using System;
@ -49,7 +47,7 @@ namespace FluidSharp.Widgets
layoutsurface.Paint(Far, farrect);
}
#if DEBUGCONTAINER
#if SHOWSPACING
layoutsurface.DebugMargin(childrect, Margin, SKColors.YellowGreen);
//layoutsurface.DebugRect(drawrect, SKColors.Blue.WithAlpha(128));
#endif

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

@ -28,10 +28,20 @@ namespace FluidSharp.Widgets
public override SKSize Measure(MeasureCache measureCache, SKSize boundaries) => InnerWidget.Measure(measureCache, boundaries);
public override SKRect PaintInternal(LayoutSurface layoutsurface, SKRect rect)
{
var translated = new SKRect(rect.Left + Translation.X, rect.Top + Translation.Y, rect.Right + Translation.X, rect.Bottom + Translation.Y);
var location = layoutsurface.Paint(InnerWidget, translated);
var inverse = new SKRect(location.Left - Translation.X, location.Top - Translation.Y, location.Right - Translation.X, location.Bottom - Translation.Y);
return inverse;
if (layoutsurface.IsRtl)
{
var translated = new SKRect(rect.Left - Translation.X, rect.Top + Translation.Y, rect.Right - Translation.X, rect.Bottom + Translation.Y);
var location = layoutsurface.Paint(InnerWidget, translated);
var inverse = new SKRect(location.Left + Translation.X, location.Top - Translation.Y, location.Right + Translation.X, location.Bottom - Translation.Y);
return inverse;
}
else
{
var translated = new SKRect(rect.Left + Translation.X, rect.Top + Translation.Y, rect.Right + Translation.X, rect.Bottom + Translation.Y);
var location = layoutsurface.Paint(InnerWidget, translated);
var inverse = new SKRect(location.Left - Translation.X, location.Top - Translation.Y, location.Right - Translation.X, location.Bottom - Translation.Y);
return inverse;
}
}
}