зеркало из https://github.com/jsuarezruiz/HotUI.git
Skia works on iOS and Mac.
This commit is contained in:
Родитель
5633dbddda
Коммит
05c7d082dc
|
@ -1,6 +0,0 @@
|
|||
namespace HotUI.Skia.iOS
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
}
|
||||
}
|
22
HotUI.sln
22
HotUI.sln
|
@ -49,6 +49,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotUI.Skia", "src\HotUI.Ski
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotUI.WPF", "src\HotUI.WPF\HotUI.WPF.csproj", "{F8BA5DE0-AEC0-4423-B036-5F9157E939D0}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Skia", "Skia", "{BDB6346D-E97A-49F4-81D6-664390944360}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotUI.Skia.iOS", "src\HotUI.Skia.iOS\HotUI.Skia.iOS.csproj", "{9ACC68A5-5A5C-409D-A379-D62220A9F49F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotUI.Skia.Mac", "src\HotUI.Skia.Mac\HotUI.Skia.Mac.csproj", "{00B248D0-382A-4504-A126-76B9C892583E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -333,6 +339,18 @@ Global
|
|||
{F8BA5DE0-AEC0-4423-B036-5F9157E939D0}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{F8BA5DE0-AEC0-4423-B036-5F9157E939D0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{F8BA5DE0-AEC0-4423-B036-5F9157E939D0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{00B248D0-382A-4504-A126-76B9C892583E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -355,8 +373,10 @@ Global
|
|||
{0FC87DE5-B5E5-4846-894B-824497118143} = {C3FDCDB7-0C29-472D-A406-E2F21A4B0EAE}
|
||||
{2D337300-B3E9-4A36-8387-76A9A1100C5B} = {AB9AD206-4B1E-4B0C-88A2-5C769314E8A4}
|
||||
{EDC997D0-2358-416F-A628-5DFD85728060} = {586C3CCB-82A5-47F0-A099-B9A31BB4EA88}
|
||||
{9ACC68A5-5A5C-409D-A379-D62220A9F49F} = {D83B3108-A837-4D1C-B66D-C4B15037BBFB}
|
||||
{9ACC68A5-5A5C-409D-A379-D62220A9F49F} = {BDB6346D-E97A-49F4-81D6-664390944360}
|
||||
{F8BA5DE0-AEC0-4423-B036-5F9157E939D0} = {AB9AD206-4B1E-4B0C-88A2-5C769314E8A4}
|
||||
{BF15264B-D7C7-4F09-B13B-487D60D649BD} = {BDB6346D-E97A-49F4-81D6-664390944360}
|
||||
{00B248D0-382A-4504-A126-76B9C892583E} = {BDB6346D-E97A-49F4-81D6-664390944360}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0653DB4A-5BBE-4D78-99B2-DB1C82663246}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HUI/@EntryIndexedValue">HUI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IUI/@EntryIndexedValue">IUI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OS/@EntryIndexedValue">OS</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UWP/@EntryIndexedValue">UWP</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WPF/@EntryIndexedValue">WPF</s:String>
|
||||
|
@ -8,6 +9,7 @@
|
|||
<s:Boolean x:Key="/Default/UserDictionary/Words/=maxx/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=midx/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=midy/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Skia/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
|
||||
</wpf:ResourceDictionary>
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
|
@ -53,6 +53,7 @@
|
|||
<Reference Include="System.Numerics.Vectors" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp.Views.Forms" Version="1.68.0" />
|
||||
<PackageReference Include="Xamarin.Android.Support.Design" Version="28.0.0.1" />
|
||||
<PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="28.0.0.1" />
|
||||
<PackageReference Include="Xamarin.Android.Support.v4" Version="28.0.0.1" />
|
||||
|
@ -106,5 +107,5 @@
|
|||
<Name>HotUI.Forms.Sample</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
</Project>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
</Project>
|
|
@ -61,8 +61,10 @@
|
|||
<MtouchArch>ARM64</MtouchArch>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
|
||||
<MtouchNoSymbolStrip></MtouchNoSymbolStrip>
|
||||
<IOSDebugOverWiFi></IOSDebugOverWiFi>
|
||||
<MtouchNoSymbolStrip>
|
||||
</MtouchNoSymbolStrip>
|
||||
<IOSDebugOverWiFi>
|
||||
</IOSDebugOverWiFi>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
@ -126,6 +128,7 @@
|
|||
<Reference Include="System.Numerics.Vectors" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp.Views.Forms" Version="1.68.0" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.2.0" />
|
||||
<PackageReference Include="Xamarin.Forms">
|
||||
<Version>4.1.0.581479</Version>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
|
@ -15,6 +15,7 @@
|
|||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp.Views.Forms" Version="1.68.0" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="4.1.0.581479" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\layout\content_main.axml">
|
||||
<SubType>Designer</SubType>
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\values\colors.xml" />
|
||||
<AndroidResource Include="Resources\values\dimens.xml" />
|
||||
|
@ -101,6 +101,8 @@
|
|||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp" Version="1.68.0" />
|
||||
<PackageReference Include="SkiaSharp.Views" Version="1.68.0" />
|
||||
<PackageReference Include="Xamarin.Android.Support.Design" Version="28.0.0.1" />
|
||||
<PackageReference Include="Xamarin.Android.Support.Core.Utils" Version="28.0.0.1" />
|
||||
<PackageReference Include="Xamarin.Android.Support.CustomTabs" Version="28.0.0.1" />
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace HotUI.Mac.Sample
|
|||
#if DEBUG
|
||||
HotUI.Reload.Init();
|
||||
#endif
|
||||
HotUI.Skia.Mac.UI.Init();
|
||||
}
|
||||
|
||||
public override void WillTerminate(NSNotification notification)
|
||||
|
|
|
@ -109,11 +109,21 @@
|
|||
<Project>{BCAF5569-30DB-4D44-BF46-DFFE93DDCBD0}</Project>
|
||||
<Name>HotUI.Mac</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\HotUI.Skia\HotUI.Skia.csproj">
|
||||
<Project>{BF15264B-D7C7-4F09-B13B-487D60D649BD}</Project>
|
||||
<Name>HotUI.Skia</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\HotUI.Skia.Mac\HotUI.Skia.Mac.csproj">
|
||||
<Project>{00B248D0-382A-4504-A126-76B9C892583E}</Project>
|
||||
<Name>HotUI.Skia.Mac</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BundleResource Include="Resources\turtlerock.jpg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp" Version="1.68.0" />
|
||||
<PackageReference Include="SkiaSharp.Views" Version="1.68.0" />
|
||||
<PackageReference Include="Xamarin.FFImageLoading" Version="2.4.11.982" />
|
||||
<PackageReference Include="HotUI.Reload">
|
||||
<Version>0.0.6-alpha</Version>
|
||||
|
|
|
@ -11,10 +11,14 @@
|
|||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\HotUI.Skia\HotUI.Skia.csproj" />
|
||||
<ProjectReference Include="..\..\src\HotUI\HotUI.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Models\" />
|
||||
<Folder Include="ApiAudit\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp" Version="1.68.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HotUI.Samples.Comparisons;
|
||||
using HotUI.Samples.Skia;
|
||||
|
||||
namespace HotUI.Samples {
|
||||
public class MainPage : View {
|
||||
|
@ -26,6 +27,10 @@ namespace HotUI.Samples {
|
|||
new MenuItem("TextFieldSample2", ()=> new TextFieldSample2()),
|
||||
new MenuItem("TextFieldSample3", ()=> new TextFieldSample3()),
|
||||
new MenuItem("TextFieldSample4", ()=> new TextFieldSample4()),
|
||||
new MenuItem("SkiaSample1 (FingerPaint)", ()=> new SkiaSample1()),
|
||||
new MenuItem("SkiaSample2 (FingerPaint)", ()=> new SkiaSample2()),
|
||||
new MenuItem("SkiaSample3 (BindableFingerPaint)", ()=> new SkiaSample3()),
|
||||
new MenuItem("SkiaSample4 (BindableFingerPaint)", ()=> new SkiaSample4()),
|
||||
new MenuItem("SwiftUI Tutorial Section 1", ()=> new Section1()),
|
||||
new MenuItem("SwiftUI Tutorial Section 2", ()=> new Section2()),
|
||||
new MenuItem("SwiftUI Tutorial Section 3", ()=> new Section3()),
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HotUI.Skia;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace HotUI.Samples.Skia
|
||||
{
|
||||
public class BindableFingerPaint : SimpleFingerPaint
|
||||
{
|
||||
private readonly List<List<PointF>> _pointsLists = new List<List<PointF>>();
|
||||
private float _strokeWidth = 2;
|
||||
private string _strokeColor = "#00FF00";
|
||||
|
||||
public BindableFingerPaint (
|
||||
Binding<float> strokeSize = null,
|
||||
Binding<string> strokeColor = null)
|
||||
{
|
||||
Bind(strokeSize, nameof(StrokeWidth), value => StrokeWidth = value);
|
||||
Bind(strokeColor, nameof(StrokeColor), value => StrokeColor = value);
|
||||
}
|
||||
|
||||
public float StrokeWidth
|
||||
{
|
||||
get => _strokeWidth;
|
||||
private set
|
||||
{
|
||||
SetValue(ref _strokeWidth, value);
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public string StrokeColor
|
||||
{
|
||||
get => _strokeColor;
|
||||
private set
|
||||
{
|
||||
SetValue(ref _strokeColor, value);
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(SKCanvas canvas, RectangleF dirtyRect)
|
||||
{
|
||||
canvas.Clear(SKColors.White);
|
||||
|
||||
var color = new Color(_strokeColor);
|
||||
|
||||
var paint = new SKPaint()
|
||||
{
|
||||
Color = color.ToSKColor(),
|
||||
StrokeWidth = StrokeWidth,
|
||||
Style = SKPaintStyle.Stroke
|
||||
};
|
||||
|
||||
foreach (var pointsList in _pointsLists)
|
||||
{
|
||||
var path = new SKPath();
|
||||
for (var i = 0; i < pointsList.Count; i++)
|
||||
{
|
||||
var point = pointsList[i];
|
||||
if (i == 0)
|
||||
path.MoveTo(point.X, point.Y);
|
||||
else
|
||||
path.LineTo(point.X, point.Y);
|
||||
}
|
||||
canvas.DrawPath(path, paint);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool StartInteraction(PointF[] points)
|
||||
{
|
||||
var pointsList = new List<PointF> {points[0]};
|
||||
_pointsLists.Add(pointsList);
|
||||
|
||||
Invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void DragInteraction(PointF[] points)
|
||||
{
|
||||
var pointsList = _pointsLists[_pointsLists.Count - 1];
|
||||
pointsList.Add(points[0]);
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public override void EndInteraction(PointF[] points)
|
||||
{
|
||||
var pointsList = _pointsLists[_pointsLists.Count - 1];
|
||||
pointsList.Add(points[0]);
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_pointsLists.Clear();
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
using System.Collections.Generic;
|
||||
using HotUI.Skia;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace HotUI.Samples.Skia
|
||||
{
|
||||
public class SimpleFingerPaint : AbstractControlDelegate
|
||||
{
|
||||
private readonly List<List<PointF>> _pointsLists = new List<List<PointF>>();
|
||||
|
||||
public override void Draw(SKCanvas canvas, RectangleF dirtyRect)
|
||||
{
|
||||
canvas.Clear(SKColors.White);
|
||||
|
||||
var paint = new SKPaint()
|
||||
{
|
||||
Color = SKColors.Blue,
|
||||
StrokeWidth = 2
|
||||
};
|
||||
|
||||
foreach (var pointsList in _pointsLists)
|
||||
{
|
||||
for (var i = 0; i < pointsList.Count; i++)
|
||||
{
|
||||
var point = pointsList[i];
|
||||
if (i > 0)
|
||||
{
|
||||
var lastPoint = pointsList[i - 1];
|
||||
canvas.DrawLine(lastPoint.X, lastPoint.Y, point.X, point.Y, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool StartInteraction(PointF[] points)
|
||||
{
|
||||
var pointsList = new List<PointF> {points[0]};
|
||||
_pointsLists.Add(pointsList);
|
||||
|
||||
Invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void DragInteraction(PointF[] points)
|
||||
{
|
||||
var pointsList = _pointsLists[_pointsLists.Count - 1];
|
||||
pointsList.Add(points[0]);
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public override void EndInteraction(PointF[] points)
|
||||
{
|
||||
var pointsList = _pointsLists[_pointsLists.Count - 1];
|
||||
pointsList.Add(points[0]);
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_pointsLists.Clear();
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace HotUI.Samples.Skia
|
||||
{
|
||||
/// <summary>
|
||||
/// This example how to use use a DrawableControl directly: you give it a control delegate
|
||||
/// in it's constructor.
|
||||
/// </summary>
|
||||
public class SkiaSample1 : View
|
||||
{
|
||||
[Body]
|
||||
View body() => new DrawableControl(new SimpleFingerPaint());
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using HotUI.Skia;
|
||||
|
||||
namespace HotUI.Samples.Skia
|
||||
{
|
||||
/// <summary>
|
||||
/// This example shows the cleaner way to use a drawable control. A control delegate can be
|
||||
/// implicitly converted into a drawable control, so there is no need to wrap it like in
|
||||
/// SkiaSample1.
|
||||
/// </summary>
|
||||
public class SkiaSample2 : View
|
||||
{
|
||||
[Body]
|
||||
View body() => new SimpleFingerPaint();
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using HotUI.Skia;
|
||||
|
||||
namespace HotUI.Samples.Skia
|
||||
{
|
||||
public class SkiaSample3 : View
|
||||
{
|
||||
readonly State<float> _strokeSize = 2;
|
||||
readonly State<string> _strokeColor = "#000000";
|
||||
|
||||
[Body]
|
||||
View body() => new VStack()
|
||||
{
|
||||
new VStack()
|
||||
{
|
||||
new HStack()
|
||||
{
|
||||
new Text("Stroke Width:"),
|
||||
new Slider(_strokeSize, 1, 10, 1)
|
||||
},
|
||||
new HStack()
|
||||
{
|
||||
new Text("Stroke Color:"),
|
||||
new TextField(_strokeColor)
|
||||
},
|
||||
new BindableFingerPaint(
|
||||
strokeSize:_strokeSize,
|
||||
strokeColor:_strokeColor).ToView().Frame(height:400)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using HotUI.Skia;
|
||||
|
||||
namespace HotUI.Samples.Skia
|
||||
{
|
||||
public class SkiaSample4 : View
|
||||
{
|
||||
readonly State<float> _strokeSize = 2;
|
||||
readonly State<string> _strokeColor = "#000000";
|
||||
|
||||
[Body]
|
||||
View body()
|
||||
{
|
||||
var fingerPaint = new BindableFingerPaint(
|
||||
strokeSize: _strokeSize,
|
||||
strokeColor: _strokeColor);
|
||||
|
||||
return new VStack()
|
||||
{
|
||||
new VStack()
|
||||
{
|
||||
new HStack()
|
||||
{
|
||||
new Text("Stroke Width:"),
|
||||
new Slider(_strokeSize, 1, 10, 1)
|
||||
},
|
||||
new HStack()
|
||||
{
|
||||
new Text("Stroke Color:"),
|
||||
new TextField(_strokeColor)
|
||||
},
|
||||
new Button("Reset", () => fingerPaint.Reset()),
|
||||
fingerPaint.ToView().Frame(height: 400)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using CoreLocation;
|
||||
using Foundation;
|
||||
using HotUI.Samples;
|
||||
|
@ -30,7 +29,9 @@ namespace HotUI.iOS.Sample {
|
|||
#if DEBUG
|
||||
HotUI.Reload.Init();
|
||||
#endif
|
||||
|
||||
|
||||
HotUI.Skia.iOS.UI.Init();
|
||||
|
||||
"turtlerock.jpg".LoadImage();
|
||||
|
||||
window = new UIWindow {
|
||||
|
|
|
@ -74,6 +74,12 @@
|
|||
<Reference Include="System.Numerics.Vectors" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp">
|
||||
<Version>1.68.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SkiaSharp.Views">
|
||||
<Version>1.68.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.2.0" />
|
||||
<PackageReference Include="Xamarin.FFImageLoading">
|
||||
<Version>2.4.11.982</Version>
|
||||
|
@ -141,6 +147,14 @@
|
|||
<Compile Include="AppDelegate.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\HotUI.Skia.iOS\HotUI.Skia.iOS.csproj">
|
||||
<Project>{9acc68a5-5a5c-409d-a379-d62220a9f49f}</Project>
|
||||
<Name>HotUI.Skia.iOS</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\HotUI.Skia\HotUI.Skia.csproj">
|
||||
<Project>{bf15264b-d7c7-4f09-b13b-487d60d649bd}</Project>
|
||||
<Name>HotUI.Skia</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\HotUI\HotUI.csproj">
|
||||
<Project>{1817646E-E71E-48CF-80A4-3030EF870D61}</Project>
|
||||
<Name>HotUI</Name>
|
||||
|
|
|
@ -6,6 +6,11 @@ namespace HotUI.Mac
|
|||
{
|
||||
public static class CoreGraphicsExtensions
|
||||
{
|
||||
public static PointF ToPointF(this CGPoint size)
|
||||
{
|
||||
return new PointF((float)size.X, (float)size.Y);
|
||||
}
|
||||
|
||||
public static SizeF ToSizeF(this CGSize size)
|
||||
{
|
||||
return new SizeF((float)size.Width, (float)size.Height);
|
||||
|
|
|
@ -8,18 +8,27 @@ namespace HotUI.Mac.Handlers
|
|||
where TVirtualView : View
|
||||
where TNativeView: NSView
|
||||
{
|
||||
private readonly PropertyMapper<TVirtualView> _mapper;
|
||||
private PropertyMapper<TVirtualView> _mapper;
|
||||
private TVirtualView _virtualView;
|
||||
private TNativeView _nativeView;
|
||||
private HUIContainerView _containerView;
|
||||
|
||||
public event EventHandler<ViewChangedEventArgs> NativeViewChanged;
|
||||
|
||||
protected AbstractControlHandler()
|
||||
{
|
||||
}
|
||||
|
||||
protected AbstractControlHandler(PropertyMapper<TVirtualView> mapper)
|
||||
{
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
protected void SetMapper(PropertyMapper<TVirtualView> mapper)
|
||||
{
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
protected abstract TNativeView CreateView();
|
||||
|
||||
protected abstract void DisposeView(TNativeView nativeView);
|
||||
|
@ -60,7 +69,7 @@ namespace HotUI.Mac.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
public SizeF Measure(SizeF availableSize)
|
||||
public virtual SizeF Measure(SizeF availableSize)
|
||||
{
|
||||
if (_nativeView is NSControl control)
|
||||
return control.SizeThatFits(availableSize.ToCGSize()).ToSizeF();
|
||||
|
@ -90,12 +99,12 @@ namespace HotUI.Mac.Handlers
|
|||
_virtualView = view as TVirtualView;
|
||||
if (_nativeView == null)
|
||||
_nativeView = CreateView();
|
||||
_mapper.UpdateProperties(this, _virtualView);
|
||||
_mapper?.UpdateProperties(this, _virtualView);
|
||||
}
|
||||
|
||||
public virtual void UpdateValue(string property, object value)
|
||||
{
|
||||
_mapper.UpdateProperty(this, _virtualView, property);
|
||||
_mapper?.UpdateProperty(this, _virtualView, property);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
using HotUI.Mac.Handlers;
|
||||
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
namespace HotUI.Skia.Mac
|
||||
{
|
||||
public class DrawableControlHandler : AbstractControlHandler<DrawableControl, MacDrawableControl>
|
||||
{
|
||||
protected override MacDrawableControl CreateView()
|
||||
{
|
||||
return new MacDrawableControl();
|
||||
}
|
||||
|
||||
protected override void DisposeView(MacDrawableControl nativeView)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void SetView(View view)
|
||||
{
|
||||
base.SetView(view);
|
||||
|
||||
SetMapper(VirtualView.ControlDelegate.Mapper);
|
||||
TypedNativeView.ControlDelegate = VirtualView.ControlDelegate;
|
||||
VirtualView.ControlDelegate.Mapper?.UpdateProperties(this, VirtualView);
|
||||
}
|
||||
|
||||
public override void Remove(View view)
|
||||
{
|
||||
TypedNativeView.ControlDelegate = null;
|
||||
SetMapper(null);
|
||||
|
||||
base.Remove(view);
|
||||
}
|
||||
|
||||
public override SizeF Measure(SizeF availableSize)
|
||||
{
|
||||
return VirtualView?.ControlDelegate?.Measure(availableSize) ?? availableSize;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{00B248D0-382A-4504-A126-76B9C892583E}</ProjectGuid>
|
||||
<ProjectTypeGuids>{A3F8F2AB-B479-4A4A-A458-A89E7DC349F1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>HotUI.Skia.Mac</RootNamespace>
|
||||
<AssemblyName>HotUI.Skia.Mac</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkIdentifier>Xamarin.Mac</TargetFrameworkIdentifier>
|
||||
<MonoMacResourcePrefix>Resources</MonoMacResourcePrefix>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<EnableCodeSigning>false</EnableCodeSigning>
|
||||
<CreatePackage>false</CreatePackage>
|
||||
<EnablePackageSigning>false</EnablePackageSigning>
|
||||
<IncludeMonoRuntime>false</IncludeMonoRuntime>
|
||||
<UseSGen>false</UseSGen>
|
||||
<HttpClientHandler>
|
||||
</HttpClientHandler>
|
||||
<LinkMode>
|
||||
</LinkMode>
|
||||
<XamMacArch>
|
||||
</XamMacArch>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<DefineConstants>
|
||||
</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<EnableCodeSigning>false</EnableCodeSigning>
|
||||
<CreatePackage>false</CreatePackage>
|
||||
<EnablePackageSigning>false</EnablePackageSigning>
|
||||
<IncludeMonoRuntime>false</IncludeMonoRuntime>
|
||||
<UseSGen>false</UseSGen>
|
||||
<HttpClientHandler>
|
||||
</HttpClientHandler>
|
||||
<LinkMode>
|
||||
</LinkMode>
|
||||
<XamMacArch>
|
||||
</XamMacArch>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Xamarin.Mac" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DrawableControlHandler.cs" />
|
||||
<Compile Include="MacDrawableControl.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="UI.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp" Version="1.68.0" />
|
||||
<PackageReference Include="SkiaSharp.Views" Version="1.68.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\HotUI.Mac\HotUI.Mac.csproj">
|
||||
<Project>{bcaf5569-30db-4d44-bf46-dffe93ddcbd0}</Project>
|
||||
<Name>HotUI.Mac</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\HotUI\HotUI.csproj">
|
||||
<Project>{1817646E-E71E-48CF-80A4-3030EF870D61}</Project>
|
||||
<Name>HotUI</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\HotUI.Skia\HotUI.Skia.csproj">
|
||||
<Project>{BF15264B-D7C7-4F09-B13B-487D60D649BD}</Project>
|
||||
<Name>HotUI.Skia</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Mac\Xamarin.Mac.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,124 @@
|
|||
using System;
|
||||
using CoreGraphics;
|
||||
using SkiaSharp.Views.Mac;
|
||||
using HotUI.Mac;
|
||||
using AppKit;
|
||||
|
||||
namespace HotUI.Skia.Mac
|
||||
{
|
||||
public class MacDrawableControl : SKCanvasView, IDrawableControl
|
||||
{
|
||||
private IControlDelegate _controlDelegate;
|
||||
|
||||
public MacDrawableControl()
|
||||
{
|
||||
}
|
||||
|
||||
public MacDrawableControl(CGRect frame) : base(frame)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsFlipped => true;
|
||||
|
||||
public IControlDelegate ControlDelegate
|
||||
{
|
||||
get => _controlDelegate;
|
||||
set
|
||||
{
|
||||
if (_controlDelegate != null)
|
||||
{
|
||||
_controlDelegate.Invalidated -= HandleInvalidated;
|
||||
_controlDelegate.RemovedFromView(this);
|
||||
_controlDelegate.NativeDrawableControl = null;
|
||||
}
|
||||
|
||||
_controlDelegate = value;
|
||||
|
||||
if (_controlDelegate != null)
|
||||
{
|
||||
_controlDelegate.AddedToView(this, Bounds.ToRectangleF());
|
||||
_controlDelegate.Invalidated += HandleInvalidated;
|
||||
_controlDelegate.NativeDrawableControl = this;
|
||||
}
|
||||
|
||||
HandleInvalidated();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleInvalidated()
|
||||
{
|
||||
if (Handle == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
NeedsDisplay = true;
|
||||
}
|
||||
|
||||
protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
|
||||
{
|
||||
if (_controlDelegate == null) return;
|
||||
var canvas = e.Surface.Canvas;
|
||||
|
||||
canvas.Save();
|
||||
var scale = CanvasSize.Width / (float)Bounds.Width;
|
||||
canvas.Scale(scale,-scale);
|
||||
canvas.Translate(0, -(float)Bounds.Height);
|
||||
_controlDelegate.Draw(canvas, Bounds.ToRectangleF());
|
||||
canvas.Restore();
|
||||
}
|
||||
|
||||
public override CGRect Frame
|
||||
{
|
||||
get => base.Frame;
|
||||
set
|
||||
{
|
||||
base.Frame = value;
|
||||
_controlDelegate?.Resized(Bounds.ToRectangleF());
|
||||
}
|
||||
}
|
||||
|
||||
public override void MouseDown(NSEvent theEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
var windowPoint = theEvent.LocationInWindow;
|
||||
var pointInView = ConvertPointFromView(windowPoint, Window.ContentView);
|
||||
var points = new PointF[] { pointInView.ToPointF() };
|
||||
_controlDelegate?.StartInteraction(points);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Error("An unexpected error occured handling a mouse event within the control.", exc);
|
||||
}
|
||||
}
|
||||
|
||||
public override void MouseDragged(NSEvent theEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
var windowPoint = theEvent.LocationInWindow;
|
||||
var pointInView = ConvertPointFromView(windowPoint, Window.ContentView);
|
||||
var points = new PointF[] { pointInView.ToPointF() };
|
||||
_controlDelegate?.DragInteraction(points);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Error("An unexpected error occured handling a mouse moved event within the control.", exc);
|
||||
}
|
||||
}
|
||||
|
||||
public override void MouseUp(NSEvent theEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
var windowPoint = theEvent.LocationInWindow;
|
||||
var pointInView = ConvertPointFromView(windowPoint, Window.ContentView);
|
||||
var points = new PointF[] { pointInView.ToPointF() };
|
||||
_controlDelegate?.EndInteraction(points);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Error("An unexpected error occured handling a mouse up event within the control.", exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Information about this assembly is defined by the following attributes.
|
||||
// Change them to the values specific to your project.
|
||||
|
||||
[assembly: AssemblyTitle("HotUI.Skia.Mac")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
// The following attributes are used to specify the signing key for the assembly,
|
||||
// if desired. See the Mono documentation for more information about signing.
|
||||
|
||||
//[assembly: AssemblyDelaySign(false)]
|
||||
//[assembly: AssemblyKeyFile("")]
|
|
@ -0,0 +1,16 @@
|
|||
namespace HotUI.Skia.Mac
|
||||
{
|
||||
public static class UI
|
||||
{
|
||||
static bool _hasInitialized;
|
||||
|
||||
public static void Init ()
|
||||
{
|
||||
if (_hasInitialized) return;
|
||||
_hasInitialized = true;
|
||||
|
||||
// Controls
|
||||
Registrar.Handlers.Register<DrawableControl, DrawableControlHandler> ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
using HotUI.iOS.Handlers;
|
||||
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
namespace HotUI.Skia.iOS
|
||||
{
|
||||
public class DrawableControlHandler : AbstractControlHandler<DrawableControl, iOSDrawableControl>
|
||||
{
|
||||
protected override iOSDrawableControl CreateView()
|
||||
{
|
||||
return new iOSDrawableControl();
|
||||
}
|
||||
|
||||
protected override void DisposeView(iOSDrawableControl nativeView)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void SetView(View view)
|
||||
{
|
||||
base.SetView(view);
|
||||
|
||||
SetMapper(VirtualView.ControlDelegate.Mapper);
|
||||
TypedNativeView.ControlDelegate = VirtualView.ControlDelegate;
|
||||
VirtualView.ControlDelegate.Mapper?.UpdateProperties(this, VirtualView);
|
||||
}
|
||||
|
||||
public override void Remove(View view)
|
||||
{
|
||||
TypedNativeView.ControlDelegate = null;
|
||||
SetMapper(null);
|
||||
|
||||
base.Remove(view);
|
||||
}
|
||||
|
||||
public override SizeF Measure(SizeF availableSize)
|
||||
{
|
||||
return VirtualView?.ControlDelegate?.Measure(availableSize) ?? availableSize;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,12 +40,28 @@
|
|||
<Folder Include="Resources\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Class1.cs" />
|
||||
<Compile Include="DrawableControlHandler.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="iOSDrawableControl.cs" />
|
||||
<Compile Include="UI.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp" Version="1.68.0" />
|
||||
<PackageReference Include="SkiaSharp.Views" Version="1.68.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\HotUI.iOS\HotUI.iOS.csproj">
|
||||
<Project>{9df36dbf-afe6-438d-b7a6-1b5fb3d1d6d1}</Project>
|
||||
<Name>HotUI.iOS</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\HotUI.Skia\HotUI.Skia.csproj">
|
||||
<Project>{bf15264b-d7c7-4f09-b13b-487d60d649bd}</Project>
|
||||
<Name>HotUI.Skia</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\HotUI\HotUI.csproj">
|
||||
<Project>{1817646e-e71e-48cf-80a4-3030ef870d61}</Project>
|
||||
<Name>HotUI</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,16 @@
|
|||
namespace HotUI.Skia.iOS
|
||||
{
|
||||
public static class UI
|
||||
{
|
||||
static bool _hasInitialized;
|
||||
|
||||
public static void Init ()
|
||||
{
|
||||
if (_hasInitialized) return;
|
||||
_hasInitialized = true;
|
||||
|
||||
// Controls
|
||||
Registrar.Handlers.Register<DrawableControl, DrawableControlHandler> ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
using SkiaSharp.Views.iOS;
|
||||
using HotUI.iOS;
|
||||
using UIKit;
|
||||
|
||||
namespace HotUI.Skia.iOS
|
||||
{
|
||||
public class iOSDrawableControl : SKCanvasView, IDrawableControl
|
||||
{
|
||||
private IControlDelegate _controlDelegate;
|
||||
|
||||
public iOSDrawableControl()
|
||||
{
|
||||
}
|
||||
|
||||
public iOSDrawableControl(CGRect frame) : base(frame)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public IControlDelegate ControlDelegate
|
||||
{
|
||||
get => _controlDelegate;
|
||||
set
|
||||
{
|
||||
if (_controlDelegate != null)
|
||||
{
|
||||
_controlDelegate.Invalidated -= HandleInvalidated;
|
||||
_controlDelegate.RemovedFromView(this);
|
||||
_controlDelegate.NativeDrawableControl = null;
|
||||
}
|
||||
|
||||
_controlDelegate = value;
|
||||
|
||||
if (_controlDelegate != null)
|
||||
{
|
||||
_controlDelegate.AddedToView(this, Bounds.ToRectangleF());
|
||||
_controlDelegate.Invalidated += HandleInvalidated;
|
||||
_controlDelegate.NativeDrawableControl = this;
|
||||
}
|
||||
|
||||
HandleInvalidated();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleInvalidated()
|
||||
{
|
||||
if (Handle == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
SetNeedsDisplay();
|
||||
}
|
||||
|
||||
protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
|
||||
{
|
||||
if (_controlDelegate == null) return;
|
||||
var canvas = e.Surface.Canvas;
|
||||
|
||||
canvas.Save();
|
||||
var scale = CanvasSize.Width / (float)Bounds.Width;
|
||||
canvas.Scale(scale,scale);
|
||||
_controlDelegate.Draw(canvas, Bounds.ToRectangleF());
|
||||
canvas.Restore();
|
||||
}
|
||||
|
||||
public override CGRect Frame
|
||||
{
|
||||
get => base.Frame;
|
||||
set
|
||||
{
|
||||
base.Frame = value;
|
||||
_controlDelegate?.Resized(Bounds.ToRectangleF());
|
||||
}
|
||||
}
|
||||
|
||||
public override void TouchesBegan(NSSet touches, UIEvent evt)
|
||||
{
|
||||
try
|
||||
{
|
||||
var viewPoints = this.GetPointsInView(evt);
|
||||
_controlDelegate?.StartInteraction(viewPoints);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Warn("An unexpected error occured handling a touch event within the control.", exc);
|
||||
}
|
||||
}
|
||||
|
||||
public override void TouchesMoved(NSSet touches, UIEvent evt)
|
||||
{
|
||||
try
|
||||
{
|
||||
var viewPoints = this.GetPointsInView(evt);
|
||||
_controlDelegate?.DragInteraction(viewPoints);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Warn("An unexpected error occured handling a touch moved event within the control.", exc);
|
||||
}
|
||||
}
|
||||
|
||||
public override void TouchesEnded(NSSet touches, UIEvent evt)
|
||||
{
|
||||
try
|
||||
{
|
||||
var viewPoints = this.GetPointsInView(evt);
|
||||
_controlDelegate?.EndInteraction(viewPoints);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Warn("An unexpected error occured handling a touch ended event within the control.", exc);
|
||||
}
|
||||
}
|
||||
|
||||
public override void TouchesCancelled(NSSet touches, UIEvent evt)
|
||||
{
|
||||
try
|
||||
{
|
||||
_controlDelegate?.CancelInteraction();
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logger.Warn("An unexpected error occured cancelling the touches within the control.", exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace HotUI.Skia
|
||||
{
|
||||
public abstract class AbstractControlDelegate : IControlDelegate
|
||||
{
|
||||
public event Action Invalidated;
|
||||
|
||||
private readonly Dictionary<string, BindingDefinition> _bindings = new Dictionary<string, BindingDefinition>();
|
||||
|
||||
protected AbstractControlDelegate()
|
||||
{
|
||||
Mapper = new PropertyMapper<DrawableControl>();
|
||||
}
|
||||
|
||||
protected AbstractControlDelegate(PropertyMapper<DrawableControl> mapper)
|
||||
{
|
||||
Mapper = mapper ?? new PropertyMapper<DrawableControl>();
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, BindingDefinition> Bindings => _bindings;
|
||||
|
||||
protected void Bind<T>(
|
||||
Binding<T> binding,
|
||||
string propertyName,
|
||||
Action<T> updater)
|
||||
{
|
||||
if (binding == null) return;
|
||||
_bindings[propertyName] = new BindingDefinition(binding, propertyName, v => updater.Invoke((T)v));
|
||||
}
|
||||
|
||||
public PropertyMapper<DrawableControl> Mapper { get; }
|
||||
|
||||
public RectangleF Bounds { get; private set; }
|
||||
|
||||
public void Invalidate()
|
||||
{
|
||||
Invalidated?.Invoke();
|
||||
}
|
||||
|
||||
public abstract void Draw(SKCanvas canvas, RectangleF dirtyRect);
|
||||
|
||||
public virtual bool StartInteraction(PointF[] points)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void DragInteraction(PointF[] points)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void EndInteraction(PointF[] points)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void CancelInteraction()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void Resized(RectangleF bounds)
|
||||
{
|
||||
Bounds = bounds;
|
||||
}
|
||||
|
||||
public virtual void AddedToView(object view, RectangleF bounds)
|
||||
{
|
||||
Bounds = bounds;
|
||||
}
|
||||
|
||||
public void RemovedFromView(object view)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DrawableControl VirtualDrawableControl { get; set; }
|
||||
|
||||
public IDrawableControl NativeDrawableControl { get; set; }
|
||||
|
||||
public virtual SizeF Measure(SizeF availableSize)
|
||||
{
|
||||
return availableSize;
|
||||
}
|
||||
|
||||
public static implicit operator View(AbstractControlDelegate controlDelegate)
|
||||
{
|
||||
return new DrawableControl(controlDelegate);
|
||||
}
|
||||
|
||||
protected void SetValue<T> (ref T currentValue, T newValue, [CallerMemberName] string propertyName = "")
|
||||
{
|
||||
VirtualDrawableControl.SetStateValue (ref currentValue, newValue , propertyName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace HotUI.Skia
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
namespace HotUI.Skia
|
||||
{
|
||||
public static class ControlDelegateExtensions
|
||||
{
|
||||
public static HotUI.View ToView(this IControlDelegate controlDelegate)
|
||||
{
|
||||
return new DrawableControl(controlDelegate);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using HotUI.Skia;
|
||||
|
||||
namespace HotUI
|
||||
{
|
||||
public class DrawableControl : BoundControl, IDrawableControl
|
||||
{
|
||||
public IControlDelegate ControlDelegate { get; set; }
|
||||
|
||||
public DrawableControl(IControlDelegate controlDelegate)
|
||||
{
|
||||
ControlDelegate = controlDelegate;
|
||||
controlDelegate.VirtualDrawableControl = this;
|
||||
|
||||
if (controlDelegate is AbstractControlDelegate abstractControlDelegate)
|
||||
AddBindings(abstractControlDelegate.Bindings);
|
||||
}
|
||||
|
||||
public void SetStateValue<T> (ref T currentValue, T newValue, [CallerMemberName] string propertyName = "")
|
||||
{
|
||||
State.SetValue (ref currentValue, newValue, this , propertyName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace HotUI.Skia
|
||||
{
|
||||
public interface IControlDelegate
|
||||
{
|
||||
event Action Invalidated;
|
||||
PropertyMapper<DrawableControl> Mapper { get; }
|
||||
RectangleF Bounds { get; }
|
||||
DrawableControl VirtualDrawableControl { get; set; }
|
||||
IDrawableControl NativeDrawableControl { get; set; }
|
||||
void Invalidate();
|
||||
void Draw(SKCanvas canvas, RectangleF dirtyRect);
|
||||
bool StartInteraction(PointF[] points);
|
||||
void DragInteraction(PointF[] points);
|
||||
void EndInteraction(PointF[] points);
|
||||
void CancelInteraction();
|
||||
void Resized(RectangleF bounds);
|
||||
void AddedToView(object view, RectangleF bounds);
|
||||
void RemovedFromView(object view);
|
||||
SizeF Measure(SizeF availableSize);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
namespace HotUI.Skia
|
||||
{
|
||||
public interface IDrawableControl
|
||||
{
|
||||
IControlDelegate ControlDelegate { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using SkiaSharp;
|
||||
|
||||
namespace HotUI.Skia
|
||||
{
|
||||
public static class SkiaGraphicsExtensions
|
||||
{
|
||||
public static SKColor ToSKColor(this Color target)
|
||||
{
|
||||
var r = (byte) (target.R * 255f);
|
||||
var g = (byte) (target.G * 255f);
|
||||
var b = (byte) (target.B * 255f);
|
||||
var a = (byte) (target.A * 255f);
|
||||
return new SKColor(r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,11 @@ namespace HotUI.iOS
|
|||
{
|
||||
public static class CoreGraphicsExtensions
|
||||
{
|
||||
public static PointF ToPointF(this CGPoint size)
|
||||
{
|
||||
return new PointF((float)size.X, (float)size.Y);
|
||||
}
|
||||
|
||||
public static SizeF ToSizeF(this CGSize size)
|
||||
{
|
||||
return new SizeF((float)size.Width, (float)size.Height);
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
using CoreGraphics;
|
||||
using UIKit;
|
||||
|
||||
namespace HotUI
|
||||
{
|
||||
public static class UIViewExtensions
|
||||
{
|
||||
public static PointF[] GetPointsInView(this UIView target, UIEvent touchEvent)
|
||||
{
|
||||
var touchSet = touchEvent.TouchesForView(target);
|
||||
if (touchSet == null || touchSet.Count == 0)
|
||||
return new PointF[0];
|
||||
|
||||
var touches = touchSet.ToArray<UITouch>();
|
||||
var points = new PointF[touches.Length];
|
||||
for (int i = 0; i < touches.Length; i++)
|
||||
{
|
||||
var touch = touches[i];
|
||||
var locationInView = touch.LocationInView(target);
|
||||
points[i] = new PointF((float) locationInView.X, (float) locationInView.Y);
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,18 +8,28 @@ namespace HotUI.iOS.Handlers
|
|||
where TVirtualView : View
|
||||
where TNativeView: UIView
|
||||
{
|
||||
private readonly PropertyMapper<TVirtualView> _mapper;
|
||||
private PropertyMapper<TVirtualView> _mapper;
|
||||
private TVirtualView _virtualView;
|
||||
private TNativeView _nativeView;
|
||||
private HUIContainerView _containerView;
|
||||
|
||||
public event EventHandler<ViewChangedEventArgs> NativeViewChanged;
|
||||
|
||||
protected AbstractControlHandler()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected AbstractControlHandler(PropertyMapper<TVirtualView> mapper)
|
||||
{
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
protected void SetMapper(PropertyMapper<TVirtualView> mapper)
|
||||
{
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
protected abstract TNativeView CreateView();
|
||||
|
||||
protected abstract void DisposeView(TNativeView nativeView);
|
||||
|
@ -62,7 +72,7 @@ namespace HotUI.iOS.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
public SizeF Measure(SizeF availableSize)
|
||||
public virtual SizeF Measure(SizeF availableSize)
|
||||
{
|
||||
return _nativeView.SizeThatFits(availableSize.ToCGSize()).ToSizeF();
|
||||
}
|
||||
|
@ -91,12 +101,13 @@ namespace HotUI.iOS.Handlers
|
|||
_virtualView = view as TVirtualView;
|
||||
if (_nativeView == null)
|
||||
_nativeView = CreateView();
|
||||
_mapper.UpdateProperties(this, _virtualView);
|
||||
|
||||
_mapper?.UpdateProperties(this, _virtualView);
|
||||
}
|
||||
|
||||
public virtual void UpdateValue(string property, object value)
|
||||
{
|
||||
_mapper.UpdateProperty(this, _virtualView, property);
|
||||
_mapper?.UpdateProperty(this, _virtualView, property);
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
<Compile Include="Controls\HUITableViewCell.cs" />
|
||||
<Compile Include="Controls\SizeChangedEventArgs.cs" />
|
||||
<Compile Include="Extensions\GraphicsExtensions.cs" />
|
||||
<Compile Include="Extensions\UIViewExtensions.cs" />
|
||||
<Compile Include="Handlers\AbstractHandler.cs" />
|
||||
<Compile Include="Handlers\AbstractLayoutHandler.cs" />
|
||||
<Compile Include="Handlers\HStackHandler.cs" />
|
||||
|
|
|
@ -3,25 +3,38 @@ using System.Linq.Expressions;
|
|||
|
||||
namespace HotUI
|
||||
{
|
||||
public class Binding<T>
|
||||
public class Binding
|
||||
{
|
||||
public Binding(Func<T> getValue, Action<T> setValue)
|
||||
public Binding(Func<object> getValue, Action<object> setValue)
|
||||
{
|
||||
GetValue = getValue;
|
||||
SetValue = setValue;
|
||||
}
|
||||
|
||||
public Func<object> GetValue { get; }
|
||||
public Action<object> SetValue { get; }
|
||||
|
||||
public bool IsValue { get; internal set; }
|
||||
public bool IsFunc { get; internal set; }
|
||||
}
|
||||
|
||||
public class Binding<T> : Binding
|
||||
{
|
||||
public Binding(Func<T> getValue, Action<T> setValue)
|
||||
: base(ToGenericGetter(getValue),ToGenericSetter(setValue))
|
||||
{
|
||||
Get = getValue;
|
||||
Set = setValue;
|
||||
}
|
||||
|
||||
|
||||
public Func<T> Get { get; }
|
||||
public Action<T> Set { get; }
|
||||
|
||||
public bool IsValue { get; private set; }
|
||||
public bool IsFunc { get; internal set; }
|
||||
|
||||
public static implicit operator Binding<T>(T value)
|
||||
{
|
||||
return new Binding<T>(
|
||||
getValue: () => value,
|
||||
setValue: null) {IsValue = true};
|
||||
setValue: null) { IsValue = true };
|
||||
}
|
||||
|
||||
public static implicit operator Binding<T>(Func<T> value)
|
||||
|
@ -30,6 +43,22 @@ namespace HotUI
|
|||
getValue: value,
|
||||
setValue: null) {IsFunc = true};
|
||||
}
|
||||
|
||||
private static Func<object> ToGenericGetter(Func<T> getValue)
|
||||
{
|
||||
if (getValue != null)
|
||||
return () => getValue.Invoke();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Action<object> ToGenericSetter(Action<T> setValue)
|
||||
{
|
||||
if (setValue != null)
|
||||
return (v) => setValue.Invoke((T)v);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class BindingExtensions
|
||||
|
|
|
@ -199,6 +199,7 @@ namespace HotUI
|
|||
isBuilding = false;
|
||||
}
|
||||
|
||||
|
||||
internal void StartProperty()
|
||||
{
|
||||
isBuilding = true;
|
||||
|
|
|
@ -1,58 +1,87 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace HotUI
|
||||
{
|
||||
public abstract class BoundControl<T> : Control
|
||||
public abstract class BoundControl : Control
|
||||
{
|
||||
private readonly Binding<T> _binding;
|
||||
private readonly string _propertyName;
|
||||
private readonly Dictionary<string, BindingDefinition> _bindings = new Dictionary<string, BindingDefinition>();
|
||||
|
||||
protected BoundControl (Binding<T> binding, string propertyName) : base(binding?.IsValue ?? false)
|
||||
protected BoundControl (params Binding[] bindings) : base(HasValueBinding(bindings))
|
||||
{
|
||||
_binding = binding;
|
||||
_propertyName = propertyName;
|
||||
|
||||
if (binding?.IsValue ?? false)
|
||||
BoundValue = binding.Get.Invoke();
|
||||
}
|
||||
|
||||
T _boundValue;
|
||||
protected T BoundValue
|
||||
protected void Bind<T>(
|
||||
Binding<T> binding,
|
||||
string propertyName,
|
||||
Action<object> updater)
|
||||
{
|
||||
get => _boundValue;
|
||||
set => SetValue (ref _boundValue, value, _propertyName);
|
||||
if (binding == null) return;
|
||||
_bindings[propertyName] = new BindingDefinition(binding, propertyName, updater);
|
||||
}
|
||||
|
||||
protected void AddBindings(IReadOnlyDictionary<string, BindingDefinition> bindings)
|
||||
{
|
||||
if (bindings == null) return;
|
||||
foreach (var entry in bindings)
|
||||
_bindings[entry.Key] = entry.Value;
|
||||
}
|
||||
|
||||
protected void SetValue<T> (ref T currentValue, T newValue, [CallerMemberName] string propertyName = "")
|
||||
{
|
||||
State.SetValue<T> (ref currentValue, newValue, this , propertyName);
|
||||
State.SetValue (ref currentValue, newValue, this , propertyName);
|
||||
}
|
||||
|
||||
protected override void WillUpdateView ()
|
||||
{
|
||||
base.WillUpdateView ();
|
||||
|
||||
if (_binding?.Get != null)
|
||||
{
|
||||
State.StartProperty ();
|
||||
var value = _binding.Get.Invoke ();
|
||||
var props = State.EndProperty ();
|
||||
var propCount = props.Length;
|
||||
if (propCount > 0)
|
||||
State.BindingState.AddViewProperty(props,this,nameof(BoundValue)) ;
|
||||
|
||||
BoundValue = value;
|
||||
if (_bindings.Count > 0)
|
||||
{
|
||||
foreach (var entry in _bindings)
|
||||
{
|
||||
var propertyName = entry.Key;
|
||||
var definition = entry.Value;
|
||||
State.StartProperty ();
|
||||
var value = definition.Binding.GetValue.Invoke ();
|
||||
definition.Updater(value);
|
||||
var props = State.EndProperty ();
|
||||
var propCount = props.Length;
|
||||
if (propCount > 0)
|
||||
State.BindingState.AddViewProperty(props, this, propertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ViewPropertyChanged(string property, object value)
|
||||
{
|
||||
if(property == nameof(BoundValue))
|
||||
{
|
||||
BoundValue = _binding.Get.Invoke();
|
||||
return;
|
||||
}
|
||||
if (_bindings.TryGetValue(property, out var definition))
|
||||
definition.Updater(value);
|
||||
|
||||
base.ViewPropertyChanged(property, value);
|
||||
}
|
||||
|
||||
private static bool HasValueBinding(Binding[] bindings)
|
||||
{
|
||||
return bindings != null && bindings.Any(b => b.IsValue);
|
||||
}
|
||||
}
|
||||
|
||||
public class BindingDefinition
|
||||
{
|
||||
public BindingDefinition(Binding binding, string propertyName, Action<object> updater)
|
||||
{
|
||||
Binding = binding;
|
||||
PropertyName = propertyName;
|
||||
Updater = updater;
|
||||
}
|
||||
|
||||
public Binding Binding { get; }
|
||||
public string PropertyName { get; }
|
||||
public Action<object> Updater { get; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace HotUI
|
||||
{
|
||||
public abstract class BoundControl<T> : Control
|
||||
{
|
||||
private readonly Binding<T> _binding;
|
||||
private readonly string _propertyName;
|
||||
|
||||
protected BoundControl (Binding<T> binding, string propertyName) : base(binding?.IsValue ?? false)
|
||||
{
|
||||
_binding = binding;
|
||||
_propertyName = propertyName;
|
||||
|
||||
if (binding?.IsValue ?? false)
|
||||
BoundValue = binding.Get.Invoke();
|
||||
}
|
||||
|
||||
T _boundValue;
|
||||
protected T BoundValue
|
||||
{
|
||||
get => _boundValue;
|
||||
set => SetValue (ref _boundValue, value, _propertyName);
|
||||
}
|
||||
|
||||
protected void SetValue<T> (ref T currentValue, T newValue, [CallerMemberName] string propertyName = "")
|
||||
{
|
||||
State.SetValue (ref currentValue, newValue, this , propertyName);
|
||||
}
|
||||
|
||||
protected override void WillUpdateView ()
|
||||
{
|
||||
base.WillUpdateView ();
|
||||
|
||||
if (_binding?.Get != null)
|
||||
{
|
||||
State.StartProperty ();
|
||||
var value = _binding.Get.Invoke ();
|
||||
var props = State.EndProperty ();
|
||||
var propCount = props.Length;
|
||||
if (propCount > 0)
|
||||
State.BindingState.AddViewProperty(props,this,nameof(BoundValue)) ;
|
||||
|
||||
BoundValue = value;
|
||||
}
|
||||
}
|
||||
protected override void ViewPropertyChanged(string property, object value)
|
||||
{
|
||||
if(property == nameof(BoundValue))
|
||||
{
|
||||
BoundValue = _binding.Get.Invoke();
|
||||
return;
|
||||
}
|
||||
base.ViewPropertyChanged(property, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using HotUI.Services;
|
||||
|
||||
namespace HotUI
|
||||
{
|
||||
public static class Logger
|
||||
{
|
||||
private static ILoggingService _registeredService;
|
||||
|
||||
public static ILoggingService RegisteredService
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_registeredService == null)
|
||||
{
|
||||
_registeredService = ServiceContainer.Resolve<ILoggingService>(true);
|
||||
if (_registeredService == null)
|
||||
{
|
||||
_registeredService = new ConsoleLoggingService();
|
||||
_registeredService.Log(LogType.Warning, "No logging service was registered. Falling back to console logging.");
|
||||
}
|
||||
}
|
||||
|
||||
return _registeredService;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterService(ILoggingService service)
|
||||
{
|
||||
ServiceContainer.Register(service);
|
||||
_registeredService = service;
|
||||
}
|
||||
|
||||
public static void Debug(params object[] parameters)
|
||||
{
|
||||
Log(LogType.Debug, parameters);
|
||||
}
|
||||
|
||||
public static void Warn(params object[] parameters)
|
||||
{
|
||||
Log(LogType.Warning, parameters);
|
||||
}
|
||||
|
||||
public static void Error(params object[] parameters)
|
||||
{
|
||||
Log(LogType.Error, parameters);
|
||||
}
|
||||
|
||||
public static void Fatal(params object[] parameters)
|
||||
{
|
||||
Log(LogType.Fatal, parameters);
|
||||
}
|
||||
|
||||
public static void Info(params object[] parameters)
|
||||
{
|
||||
Log(LogType.Info, parameters);
|
||||
}
|
||||
|
||||
public static void Log(LogType logType, params object[] parameters)
|
||||
{
|
||||
if (parameters == null || parameters.Length == 0)
|
||||
return;
|
||||
|
||||
if (parameters.Length == 1)
|
||||
{
|
||||
if (parameters[0] is Exception exception)
|
||||
{
|
||||
RegisteredService.Log(logType, exception.Message, exception);
|
||||
return;
|
||||
}
|
||||
|
||||
var value = parameters[0];
|
||||
if (value != null)
|
||||
{
|
||||
RegisteredService.Log(logType, value.ToString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var format = parameters[0] != null ? parameters[0].ToString() : "";
|
||||
var message = format;
|
||||
|
||||
try
|
||||
{
|
||||
var args = new object[parameters.Length - 1];
|
||||
Array.Copy(parameters, 1, args, 0, parameters.Length - 1);
|
||||
|
||||
message = string.Format(format, args);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
RegisteredService.Log(LogType.Info, $"An error occured formatting the logging message: [{format}]", exc);
|
||||
}
|
||||
|
||||
if (parameters[parameters.Length - 1] is Exception ex)
|
||||
{
|
||||
RegisteredService.Log(logType, message, ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
RegisteredService.Log(logType, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace HotUI
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple service container implementation, singleton only
|
||||
/// </summary>
|
||||
public static class ServiceContainer
|
||||
{
|
||||
static readonly ConcurrentDictionary<Type, Lazy<object>> services = new ConcurrentDictionary<Type, Lazy<object>>();
|
||||
static readonly Stack<Dictionary<Type, object>> scopedServices = new Stack<Dictionary<Type, object>>();
|
||||
|
||||
/// <summary>
|
||||
/// Register the specified service with an instance
|
||||
/// </summary>
|
||||
public static void Register<T>(T service)
|
||||
{
|
||||
services[typeof(T)] = new Lazy<object>(() => service);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register the specified service for a class with a default constructor
|
||||
/// </summary>
|
||||
public static void Register<T>() where T : new()
|
||||
{
|
||||
services[typeof(T)] = new Lazy<object>(() => new T(), LazyThreadSafetyMode.ExecutionAndPublication);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register the specified service with a callback to be invoked when requested
|
||||
/// </summary>
|
||||
public static void Register<T>(Func<T> function)
|
||||
{
|
||||
services[typeof(T)] = new Lazy<object>(() => function());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register the specified service with an instance
|
||||
/// </summary>
|
||||
public static void Register(Type type, object service)
|
||||
{
|
||||
services[type] = new Lazy<object>(() => service);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register the specified service with a callback to be invoked when requested
|
||||
/// </summary>
|
||||
public static void Register(Type type, Func<object> function)
|
||||
{
|
||||
services[type] = new Lazy<object>(function, LazyThreadSafetyMode.ExecutionAndPublication);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register the specified service with an instance that is scoped
|
||||
/// </summary>
|
||||
public static void RegisterScoped<T>(T service)
|
||||
{
|
||||
Dictionary<Type, object> scope;
|
||||
if (scopedServices.Count == 0)
|
||||
{
|
||||
scope = new Dictionary<Type, object>();
|
||||
scopedServices.Push(scope);
|
||||
}
|
||||
else
|
||||
{
|
||||
scope = scopedServices.Peek();
|
||||
}
|
||||
|
||||
scope[typeof(T)] = service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the type, throwing an exception if not found
|
||||
/// </summary>
|
||||
public static T Resolve<T>(bool nullIsAcceptable = false)
|
||||
{
|
||||
return (T) Resolve(typeof(T), nullIsAcceptable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the type, throwing an exception if not found
|
||||
/// </summary>
|
||||
public static object Resolve(Type type, bool nullIsAcceptable = false)
|
||||
{
|
||||
//Scoped services
|
||||
if (scopedServices.Count > 0)
|
||||
{
|
||||
var scope = scopedServices.Peek();
|
||||
|
||||
object service;
|
||||
if (scope.TryGetValue(type, out service))
|
||||
{
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
//Non-scoped services
|
||||
{
|
||||
Lazy<object> service;
|
||||
if (services.TryGetValue(type, out service))
|
||||
{
|
||||
return service.Value;
|
||||
}
|
||||
|
||||
if (nullIsAcceptable) return null;
|
||||
|
||||
throw new KeyNotFoundException(string.Format("Service not found for type '{0}'", type));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a "scope" which is a way to register a service on a stack to be popped off at a later time
|
||||
/// </summary>
|
||||
public static void AddScope()
|
||||
{
|
||||
scopedServices.Push(new Dictionary<Type, object>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the current "scope" which pops off some local services
|
||||
/// </summary>
|
||||
public static void RemoveScope()
|
||||
{
|
||||
if (scopedServices.Count > 0)
|
||||
scopedServices.Pop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mainly for testing, clears the entire container
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
services.Clear();
|
||||
scopedServices.Clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace HotUI.Services
|
||||
{
|
||||
public class ConsoleLoggingService : ILoggingService
|
||||
{
|
||||
public virtual void Log(LogType type, string message)
|
||||
{
|
||||
WriteToConsole(type, message);
|
||||
}
|
||||
|
||||
public virtual void Log(LogType type, string message, Exception exception)
|
||||
{
|
||||
WriteToConsole(type, message + "\n" + GetExceptionDetails(exception));
|
||||
}
|
||||
|
||||
protected virtual void WriteToConsole(LogType type, string message)
|
||||
{
|
||||
Console.WriteLine("[{0}] {1}", type, message);
|
||||
}
|
||||
|
||||
protected virtual string GetExceptionDetails(Exception exception)
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
|
||||
writer.Write("Exception: ");
|
||||
writer.WriteLine(exception.GetType());
|
||||
writer.Write("Message: ");
|
||||
writer.WriteLine(exception.Message);
|
||||
if (exception.InnerException != null)
|
||||
{
|
||||
writer.Write("InnerException: ");
|
||||
writer.WriteLine(exception.InnerException);
|
||||
}
|
||||
|
||||
writer.Write("StackTrace: ");
|
||||
writer.WriteLine(exception.StackTrace);
|
||||
|
||||
return writer.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace HotUI.Services
|
||||
{
|
||||
public enum LogType
|
||||
{
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
Fatal
|
||||
}
|
||||
|
||||
public interface ILoggingService
|
||||
{
|
||||
void Log(LogType logType, string message);
|
||||
void Log(LogType logType, string message, Exception exception);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче