This commit is contained in:
Jon Lipsky 2019-07-19 15:58:35 -05:00
Родитель 5633dbddda
Коммит 05c7d082dc
51 изменённых файлов: 1556 добавлений и 70 удалений

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

@ -1,6 +0,0 @@
namespace HotUI.Skia.iOS
{
public class Class1
{
}
}

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

@ -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("")]

16
src/HotUI.Skia.Mac/UI.cs Normal file
Просмотреть файл

@ -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>

16
src/HotUI.Skia.iOS/UI.cs Normal file
Просмотреть файл

@ -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);
}
}
}

105
src/HotUI/Logger.cs Normal file
Просмотреть файл

@ -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);
}
}