Browse Source

Somewhat fixed iOS. Now also using framebuffer model

pull/885/head
Nikita Tsukanov 9 years ago
parent
commit
7e163ef310
  1. 34
      Avalonia.sln
  2. 11
      samples/ControlCatalog.iOS/AppDelegate.cs
  3. 17
      samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
  4. 18
      src/Shared/PlatformSupport/AssetLoader.cs
  5. 74
      src/Skia/Avalonia.Skia.iOS.TestApp/AppDelegate.cs
  6. 143
      src/Skia/Avalonia.Skia.iOS.TestApp/Avalonia.Skia.iOS.TestApp.csproj
  7. 26
      src/Skia/Avalonia.Skia.iOS.TestApp/Avalonia.Skia.iOS.TestApp.v2.ncrunchproject
  8. 5
      src/Skia/Avalonia.Skia.iOS.TestApp/Entitlements.plist
  9. 4
      src/Skia/Avalonia.Skia.iOS.TestApp/GettingStarted.Xamarin
  10. 42
      src/Skia/Avalonia.Skia.iOS.TestApp/Info.plist
  11. 15
      src/Skia/Avalonia.Skia.iOS.TestApp/Main.cs
  12. 77
      src/Skia/Avalonia.Skia.iOS.TestApp/MainView.cs
  13. 36
      src/Skia/Avalonia.Skia.iOS.TestApp/Properties/AssemblyInfo.cs
  14. 43
      src/Skia/Avalonia.Skia.iOS.TestApp/Resources/LaunchScreen.xib
  15. 6
      src/Skia/Avalonia.Skia.iOS/PlatformRenderingInterfaceIos.cs
  16. 21
      src/iOS/Avalonia.iOS/AppBuilder.cs
  17. 11
      src/iOS/Avalonia.iOS/Avalonia.iOS.csproj
  18. 58
      src/iOS/Avalonia.iOS/AvaloniaRootViewController.cs
  19. 248
      src/iOS/Avalonia.iOS/AvaloniaView.cs
  20. 31
      src/iOS/Avalonia.iOS/AvaloniaWindow.cs
  21. 35
      src/iOS/Avalonia.iOS/DisplayLinkRenderLoop.cs
  22. 28
      src/iOS/Avalonia.iOS/EmbeddableImpl.cs
  23. 59
      src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs
  24. 10
      src/iOS/Avalonia.iOS/Extensions.cs
  25. 106
      src/iOS/Avalonia.iOS/PlatformThreadingInterface.cs
  26. 2
      src/iOS/Avalonia.iOS/Specific/KeyboardEventsHelper.cs
  27. 192
      src/iOS/Avalonia.iOS/TopLevelImpl.cs
  28. 14
      src/iOS/Avalonia.iOS/WindowingPlatformImpl.cs
  29. 14
      src/iOS/Avalonia.iOS/iOSPlatform.cs
  30. 16
      src/iOS/Avalonia.iOSTestApplication/AppDelegate.cs
  31. 6
      src/iOS/Avalonia.iOSTestApplication/Avalonia.iOSTestApplication.csproj
  32. 6
      src/iOS/Avalonia.iOSTestApplication/SimpleApp.xaml
  33. 20
      src/iOS/Avalonia.iOSTestApplication/SimpleApp.xaml.cs
  34. 20
      src/iOS/Avalonia.iOSTestApplication/SimpleControl.cs

34
Avalonia.sln

@ -99,8 +99,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Skia.Desktop", "sr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Skia.Android", "src\Skia\Avalonia.Skia.Android\Avalonia.Skia.Android.csproj", "{BD43F7C0-396B-4AA1-BAD9-DFDE54D51298}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Skia.iOS.TestApp", "src\Skia\Avalonia.Skia.iOS.TestApp\Avalonia.Skia.iOS.TestApp.csproj", "{DA49C5F3-BE95-461C-B999-072128CCF59E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Skia.iOS", "src\Skia\Avalonia.Skia.iOS\Avalonia.Skia.iOS.csproj", "{47BE08A7-5985-410B-9FFC-2264B8EA595F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}"
@ -178,15 +176,12 @@ Global
tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{48840edd-24bf-495d-911e-2eb12ae75d3b}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{7863ea94-f0fb-4386-bf8c-e5bfa761560a}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 4
src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 4
src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{9defc6b7-845b-4d8f-afc0-d32bf0032b8c}*SharedItemsImports = 13
src\Shared\RenderHelpers\RenderHelpers.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{bd43f7c0-396b-4aa1-bad9-dfde54d51298}*SharedItemsImports = 4
tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{d35a9f3d-8bb0-496e-bf72-444038a7debb}*SharedItemsImports = 4
tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{dabfd304-d6a4-4752-8123-c2ccf7ac7831}*SharedItemsImports = 4
tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{e106cf37-4066-4615-b684-172a6d30b058}*SharedItemsImports = 4
@ -1486,34 +1481,6 @@ Global
{BD43F7C0-396B-4AA1-BAD9-DFDE54D51298}.Release|Mono.ActiveCfg = Release|Any CPU
{BD43F7C0-396B-4AA1-BAD9-DFDE54D51298}.Release|x86.ActiveCfg = Release|Any CPU
{BD43F7C0-396B-4AA1-BAD9-DFDE54D51298}.Release|x86.Build.0 = Release|Any CPU
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Ad-Hoc|Mono.ActiveCfg = Ad-Hoc|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Ad-Hoc|x86.ActiveCfg = Ad-Hoc|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.AppStore|iPhone.Build.0 = AppStore|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.AppStore|Mono.ActiveCfg = AppStore|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.AppStore|x86.ActiveCfg = AppStore|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Debug|iPhone.ActiveCfg = Debug|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Debug|iPhone.Build.0 = Debug|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Debug|Mono.ActiveCfg = Debug|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Debug|x86.ActiveCfg = Debug|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Release|Any CPU.ActiveCfg = Release|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Release|iPhone.ActiveCfg = Release|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Release|iPhone.Build.0 = Release|iPhone
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Release|Mono.ActiveCfg = Release|iPhoneSimulator
{DA49C5F3-BE95-461C-B999-072128CCF59E}.Release|x86.ActiveCfg = Release|iPhoneSimulator
{47BE08A7-5985-410B-9FFC-2264B8EA595F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{47BE08A7-5985-410B-9FFC-2264B8EA595F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{47BE08A7-5985-410B-9FFC-2264B8EA595F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@ -2502,7 +2469,6 @@ Global
{2F59F3D0-748D-4652-B01E-E0D954756308} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{925DD807-B651-475F-9F7C-CBEB974CE43D} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{BD43F7C0-396B-4AA1-BAD9-DFDE54D51298} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{DA49C5F3-BE95-461C-B999-072128CCF59E} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{47BE08A7-5985-410B-9FFC-2264B8EA595F} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{7B92AF71-6287-4693-9DCB-BD5B6E927E23} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}
{FF69B927-C545-49AE-8E16-3D14D621AA12} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F}

11
samples/ControlCatalog.iOS/AppDelegate.cs

@ -2,6 +2,8 @@ using Foundation;
using UIKit;
using Avalonia;
using Avalonia.Controls;
using Avalonia.iOS;
using Avalonia.Media;
namespace ControlCatalog
{
@ -11,6 +13,8 @@ namespace ControlCatalog
[Register("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
public override UIWindow Window { get; set; }
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
@ -22,10 +26,9 @@ namespace ControlCatalog
{
AppBuilder.Configure<App>()
.UseiOS()
.UseSkiaViewHost()
.UseSkia()
.Start<MainWindow>();
.UseSkia().SetupWithoutStarting();
Window = new AvaloniaWindow() {Content = new MainView(), StatusBarColor = Colors.LightSteelBlue};
Window.MakeKeyAndVisible();
return true;
}
}

17
samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj

@ -20,8 +20,21 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>i386</MtouchArch>
<MtouchLink>None</MtouchLink>
<MtouchDebug>true</MtouchDebug>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchDebug>True</MtouchDebug>
<MtouchSdkVersion>9.1</MtouchSdkVersion>
<MtouchProfiling>False</MtouchProfiling>
<MtouchFastDev>False</MtouchFastDev>
<MtouchNoSymbolStrip>False</MtouchNoSymbolStrip>
<MtouchUseLlvm>False</MtouchUseLlvm>
<MtouchUseThumb>False</MtouchUseThumb>
<MtouchEnableBitcode>False</MtouchEnableBitcode>
<MtouchUseSGen>False</MtouchUseSGen>
<MtouchUseRefCounting>False</MtouchUseRefCounting>
<OptimizePNGs>True</OptimizePNGs>
<MtouchTlsProvider>Default</MtouchTlsProvider>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchFloat32>False</MtouchFloat32>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
<DebugType>none</DebugType>

18
src/Shared/PlatformSupport/AssetLoader.cs

@ -85,7 +85,7 @@ namespace Avalonia.Shared.PlatformSupport
{
var asm = GetAssembly(uri) ?? GetAssembly(baseUri) ?? _defaultAssembly;
if (asm == null && _defaultAssembly == null)
if (asm == null)
{
throw new ArgumentException(
"No default assembly, entry assembly or explicit assembly specified; " +
@ -95,17 +95,6 @@ namespace Avalonia.Shared.PlatformSupport
IAssetDescriptor rv;
var resourceKey = uri.AbsolutePath;
#if __IOS__
// TODO: HACK: to get iOS up and running. Using Shared projects for resources
// is flawed as this alters the reource key locations across platforms
// I think we need to use Portable libraries from now on to avoid that.
if(asm.Name.Contains("iOS"))
{
resourceKey = resourceKey.Replace("TestApplication", "Avalonia.iOSTestApplication");
}
#endif
asm.Resources.TryGetValue(resourceKey, out rv);
return rv;
}
@ -150,7 +139,10 @@ namespace Avalonia.Shared.PlatformSupport
//
#if NETSTANDARD
AssemblyNameCache[name] = rv = new AssemblyDescriptor(Assembly.Load(new AssemblyName(name)));
#elif !__IOS__
#elif __IOS__
throw new InvalidOperationException(
$"Assembly {name} needs to be referenced and explicitly loaded before loading resources");
#else
AssemblyNameCache[name] = rv = new AssemblyDescriptor(Assembly.Load(name));
#endif
}

74
src/Skia/Avalonia.Skia.iOS.TestApp/AppDelegate.cs

@ -1,74 +0,0 @@
using Foundation;
using UIKit;
namespace Avalonia.Skia.iOS.TestApp
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to application events from iOS.
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
// class-level declarations
public override UIWindow Window
{
get;
set;
}
class Controller : UIViewController
{
public override void LoadView()
{
View = new MainView();
}
}
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
// If you have defined a root view controller, set it here:
Window.RootViewController = new Controller();
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
public override void OnResignActivation(UIApplication application)
{
// Invoked when the application is about to move from active to inactive state.
// This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message)
// or when the user quits the application and it begins the transition to the background state.
// Games should use this method to pause the game.
}
public override void DidEnterBackground(UIApplication application)
{
// Use this method to release shared resources, save user data, invalidate timers and store the application state.
// If your application supports background exection this method is called instead of WillTerminate when the user quits.
}
public override void WillEnterForeground(UIApplication application)
{
// Called as part of the transiton from background to active state.
// Here you can undo many of the changes made on entering the background.
}
public override void OnActivated(UIApplication application)
{
// Restart any tasks that were paused (or not yet started) while the application was inactive.
// If the application was previously in the background, optionally refresh the user interface.
}
public override void WillTerminate(UIApplication application)
{
// Called when the application is about to terminate. Save data, if needed. See also DidEnterBackground.
}
}
}

143
src/Skia/Avalonia.Skia.iOS.TestApp/Avalonia.Skia.iOS.TestApp.csproj

@ -1,143 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{DA49C5F3-BE95-461C-B999-072128CCF59E}</ProjectGuid>
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Exe</OutputType>
<RootNamespace>Avalonia.Skia.iOS.TestApp</RootNamespace>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<AssemblyName>AvaloniaSkiaiOSTestApp</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>i386</MtouchArch>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchDebug>true</MtouchDebug>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MtouchLink>None</MtouchLink>
<MtouchArch>i386</MtouchArch>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhone\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>true</MtouchDebug>
<CodesignProvision>
</CodesignProvision>
<CodesignResourceRules />
<CodesignExtraArgs />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhone\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<ConsolePause>false</ConsolePause>
<CodesignKey>iPhone Developer</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Ad-Hoc|iPhone' ">
<DebugType>none</DebugType>
<Optimize>True</Optimize>
<OutputPath>bin\iPhone\Ad-Hoc</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<BuildIpa>True</BuildIpa>
<CodesignProvision>Automatic:AdHoc</CodesignProvision>
<CodesignKey>iPhone Distribution</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'AppStore|iPhone' ">
<DebugType>none</DebugType>
<Optimize>True</Optimize>
<OutputPath>bin\iPhone\AppStore</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<CodesignProvision>Automatic:AppStore</CodesignProvision>
<CodesignKey>iPhone Distribution</CodesignKey>
</PropertyGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<None Include="GettingStarted.Xamarin" />
<None Include="Info.plist" />
<Compile Include="MainView.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<InterfaceDefinition Include="Resources\LaunchScreen.xib" />
<Content Include="Entitlements.plist" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Xamarin.iOS" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\Avalonia.Skia.iOS\Avalonia.Skia.iOS.csproj">
<Project>{47be08a7-5985-410b-9ffc-2264b8ea595f}</Project>
<Name>Avalonia.Skia.iOS</Name>
<IsAppExtension>false</IsAppExtension>
<IsWatchApp>false</IsWatchApp>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project>

26
src/Skia/Avalonia.Skia.iOS.TestApp/Avalonia.Skia.iOS.TestApp.v2.ncrunchproject

@ -1,26 +0,0 @@
<ProjectConfiguration>
<AutoDetectNugetBuildDependencies>true</AutoDetectNugetBuildDependencies>
<BuildPriority>1000</BuildPriority>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<AllowCodeAnalysis>false</AllowCodeAnalysis>
<IgnoreThisComponentCompletely>true</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents>
<RunPostBuildEvents>false</RunPostBuildEvents>
<PreviouslyBuiltSuccessfully>true</PreviouslyBuiltSuccessfully>
<InstrumentAssembly>true</InstrumentAssembly>
<PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<DetectStackOverflow>true</DetectStackOverflow>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout>
<UseBuildConfiguration></UseBuildConfiguration>
<UseBuildPlatform></UseBuildPlatform>
<ProxyProcessPath></ProxyProcessPath>
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture>
<MSTestThreadApartmentState>STA</MSTestThreadApartmentState>
<BuildProcessArchitecture>x86</BuildProcessArchitecture>
</ProjectConfiguration>

5
src/Skia/Avalonia.Skia.iOS.TestApp/Entitlements.plist

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

4
src/Skia/Avalonia.Skia.iOS.TestApp/GettingStarted.Xamarin

@ -1,4 +0,0 @@
<GettingStarted>
<LocalContent>GS\iOS\CS\iOSApp\GettingStarted.html</LocalContent>
<EmbeddedNavigation>false</EmbeddedNavigation>
</GettingStarted>

42
src/Skia/Avalonia.Skia.iOS.TestApp/Info.plist

@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>Avalonia.Skia.iOS.TestApp</string>
<key>CFBundleIdentifier</key>
<string>com.companyname.Avalonia.Skia.iOS.TestApp</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>8.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

15
src/Skia/Avalonia.Skia.iOS.TestApp/Main.cs

@ -1,15 +0,0 @@
using UIKit;
namespace Avalonia.Skia.iOS.TestApp
{
public class Application
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, "AppDelegate");
}
}
}

77
src/Skia/Avalonia.Skia.iOS.TestApp/MainView.cs

@ -1,77 +0,0 @@
using System;
using System.Diagnostics;
using System.Drawing;
using CoreAnimation;
using CoreGraphics;
using CoreMedia;
using Foundation;
using Avalonia.Media;
using Avalonia.Platform;
using UIKit;
namespace Avalonia.Skia.iOS.TestApp
{
[Register("MainView")]
public class MainView : SkiaView
{
private IRenderTarget _target;
FormattedText _text;
public MainView()
{
AutoresizingMask = UIViewAutoresizing.All;
SkiaPlatform.Initialize();
_target = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
.CreateRenderTarget(AvaloniaPlatformHandle);
UpdateText(0);
}
double _radians = 0;
void UpdateText(int fps)
{
_text?.Dispose();
_text = new FormattedText("FPS: " + fps, "Arial", 15, FontStyle.Normal, TextAlignment.Left,
FontWeight.Normal);
}
double _lastFps;
int _frames;
Stopwatch St = Stopwatch.StartNew();
protected override void Draw()
{
_radians += 0.02;
var scale = UIScreen.MainScreen.Scale;
int width = (int) (Bounds.Width*scale), height = (int) (Bounds.Height*scale);
using (var ctx = _target.CreateDrawingContext())
{
ctx.FillRectangle(Brushes.Green, new Rect(0, 0, width, height));
ctx.DrawText(Brushes.Red, new Point(50, 50), _text);
var rc = new Rect(0, 0, width/3, height/3);
using (ctx.PushPostTransform(
Avalonia.Matrix.CreateTranslation(-width/6, -width/6)*
Avalonia.Matrix.CreateRotation(_radians)*
Avalonia.Matrix.CreateTranslation(width/2, height/2)))
{
ctx.FillRectangle(new LinearGradientBrush()
{
GradientStops =
{
new GradientStop() {Color = Colors.Blue},
new GradientStop(Colors.Red, 1)
}
}, rc, 5);
}
}
_frames++;
var now = St.Elapsed.TotalSeconds;
var elapsed = now - _lastFps;
if (elapsed > 1)
{
UpdateText((int) (_frames/elapsed));
_frames = 0;
_lastFps = now;
}
DrawOnNextFrame();
}
}
}

36
src/Skia/Avalonia.Skia.iOS.TestApp/Properties/AssemblyInfo.cs

@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.Skia.iOS.TestApp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Avalonia.Skia.iOS.TestApp")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("da49c5f3-be95-461c-b999-072128ccf59e")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

43
src/Skia/Avalonia.Skia.iOS.TestApp/Resources/LaunchScreen.xib

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207" />
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1" />
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" />
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder" />
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480" />
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" />
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines"
minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21" />
<fontDescription key="fontDescription" type="system" pointSize="17" />
<color key="textColor" cocoaTouchSystemColor="darkTextColor" />
<nil key="highlightedColor" />
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Avalonia.Skia.iOS.TestApp" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines"
minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43" />
<fontDescription key="fontDescription" type="boldSystem" pointSize="36" />
<color key="textColor" cocoaTouchSystemColor="darkTextColor" />
<nil key="highlightedColor" />
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite" />
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC" />
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk" />
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l" />
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0" />
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9" />
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g" />
</constraints>
<nil key="simulatedStatusBarMetrics" />
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics" />
<point key="canvasLocation" x="548" y="455" />
</view>
</objects>
</document>

6
src/Skia/Avalonia.Skia.iOS/PlatformRenderingInterfaceIos.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
using Foundation;
using UIKit;
@ -12,7 +13,10 @@ namespace Avalonia.Skia
{
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
return new WindowRenderTarget();
var fb = surfaces?.OfType<IFramebufferPlatformSurface>().FirstOrDefault();
if (fb == null)
throw new Exception("Avalonia.Skia.Deskop currently only supports framebuffer render target");
return new FramebufferRenderTarget(fb);
}
}
}

21
src/iOS/Avalonia.iOS/AppBuilder.cs

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Shared.PlatformSupport;
using Foundation;
using UIKit;
namespace Avalonia
{
public class AppBuilder : AppBuilderBase<AppBuilder>
{
public AppBuilder() : base(new StandardRuntimePlatform(),
builder => StandardRuntimePlatformServices.Register(builder.Instance?.GetType().Assembly))
{
}
}
}

11
src/iOS/Avalonia.iOS/Avalonia.iOS.csproj

@ -23,6 +23,7 @@
<ConsolePause>false</ConsolePause>
<MtouchDebug>true</MtouchDebug>
<CodesignKey>iPhone Developer</CodesignKey>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@ -32,13 +33,21 @@
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<CodesignKey>iPhone Developer</CodesignKey>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="AppBuilder.cs" />
<Compile Include="AvaloniaRootViewController.cs" />
<Compile Include="AvaloniaView.cs" />
<Compile Include="AvaloniaWindow.cs" />
<Compile Include="Clipboard.cs" />
<Compile Include="CursorFactory.cs" />
<Compile Include="DisplayLinkRenderLoop.cs" />
<Compile Include="EmbeddableImpl.cs" />
<Compile Include="EmulatedFramebuffer.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="iOSPlatform.cs" />
<Compile Include="AvaloniaView.cs" />
<Compile Include="TopLevelImpl.cs" />
<Compile Include="PlatformIconLoader.cs" />
<Compile Include="PlatformSettings.cs" />
<Compile Include="PlatformThreadingInterface.cs" />

58
src/iOS/Avalonia.iOS/AvaloniaRootViewController.cs

@ -0,0 +1,58 @@
using Avalonia.Media;
using CoreGraphics;
using UIKit;
namespace Avalonia.iOS
{
class AvaloniaRootViewController : UIViewController
{
private object _content;
private Color _statusBarColor = Colors.White;
public object Content
{
get { return _content; }
set
{
_content = value;
var view = (View as AvaloniaView);
if (view != null)
view.Content = value;
}
}
public Color StatusBarColor
{
get { return _statusBarColor; }
set
{
_statusBarColor = value;
var view = (View as AvaloniaView);
if (view != null)
view.BackgroundColor = value.ToUiColor();
}
}
void AutoFit()
{
var needFlip = !UIDevice.CurrentDevice.CheckSystemVersion(8, 0) &&
(InterfaceOrientation == UIInterfaceOrientation.LandscapeLeft
|| InterfaceOrientation == UIInterfaceOrientation.LandscapeRight);
// Bounds here (if top level) needs to correspond with the rendertarget
var frame = UIScreen.MainScreen.Bounds;
if (needFlip)
frame = new CGRect(frame.Y, frame.X, frame.Height, frame.Width);
((AvaloniaView) View).Padding =
new Thickness(0, UIApplication.SharedApplication.StatusBarFrame.Size.Height, 0, 0);
View.Frame = frame;
}
public override void LoadView()
{
View = new AvaloniaView() {Content = Content, BackgroundColor = _statusBarColor.ToUiColor()};
UIApplication.Notifications.ObserveDidChangeStatusBarOrientation(delegate { AutoFit(); });
UIApplication.Notifications.ObserveDidChangeStatusBarFrame(delegate { AutoFit(); });
AutoFit();
}
}
}

248
src/iOS/Avalonia.iOS/AvaloniaView.cs

@ -1,249 +1,51 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Disposables;
using System.Text;
using CoreAnimation;
using Avalonia.Controls.Embedding;
using CoreGraphics;
using Foundation;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Skia.iOS;
using UIKit;
using Avalonia.iOS.Specific;
using ObjCRuntime;
using Avalonia.Controls;
namespace Avalonia.iOS
{
[Adopts("UIKeyInput")]
class AvaloniaView : SkiaView, IWindowImpl
public class AvaloniaView : UIView
{
private readonly UIWindow _window;
private readonly UIViewController _controller;
private IInputRoot _inputRoot;
private readonly KeyboardEventsHelper<AvaloniaView> _keyboardHelper;
private Point _position;
private EmbeddableImpl _impl;
private EmbeddableControlRoot _root;
private Thickness _padding;
public AvaloniaView(UIWindow window, UIViewController controller) : base(onFrame => PlatformThreadingInterface.Instance.Render = onFrame)
public Thickness Padding
{
if (controller == null) throw new ArgumentNullException(nameof(controller));
_window = window;
_controller = controller;
_keyboardHelper = new KeyboardEventsHelper<AvaloniaView>(this);
AutoresizingMask = UIViewAutoresizing.All;
AutoFit();
UIApplication.Notifications.ObserveDidChangeStatusBarOrientation(delegate { AutoFit(); });
UIApplication.Notifications.ObserveDidChangeStatusBarFrame(delegate { AutoFit(); });
}
[Export("hasText")]
bool HasText => _keyboardHelper.HasText();
[Export("insertText:")]
void InsertText(string text) => _keyboardHelper.InsertText(text);
[Export("deleteBackward")]
void DeleteBackward() => _keyboardHelper.DeleteBackward();
public override bool CanBecomeFirstResponder => _keyboardHelper.CanBecomeFirstResponder();
void AutoFit()
{
var needFlip = !UIDevice.CurrentDevice.CheckSystemVersion(8, 0) &&
(_controller.InterfaceOrientation == UIInterfaceOrientation.LandscapeLeft
|| _controller.InterfaceOrientation == UIInterfaceOrientation.LandscapeRight);
// Bounds here (if top level) needs to correspond with the rendertarget
var frame = UIScreen.MainScreen.Bounds;
if (needFlip)
Frame = new CGRect(frame.Y, frame.X, frame.Height, frame.Width);
else
Frame = frame;
}
public Action Activated { get; set; }
public Action Closed { get; set; }
public Action Deactivated { get; set; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public Action<Point> PositionChanged { get; set; }
public IPlatformHandle Handle => AvaloniaPlatformHandle;
public double Scaling
{
get
{
// This does not appear to make any difference, but on iOS we
// have Retina (x2) and we probably want this eventually
return 1; //UIScreen.MainScreen.Scale;
}
}
public WindowState WindowState
{
get { return WindowState.Normal; }
set { }
}
public override void LayoutSubviews() => Resized?.Invoke(ClientSize);
public Size ClientSize
{
get { return Bounds.Size.ToAvalonia(); }
set { Resized?.Invoke(ClientSize); }
}
public void Activate()
{
}
protected override void Draw()
{
Paint?.Invoke(new Rect(new Point(), ClientSize));
}
public void Invalidate(Rect rect) => DrawOnNextFrame();
public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
public Point PointToClient(Point point) => point;
public Point PointToScreen(Point point) => point;
public void SetCursor(IPlatformHandle cursor)
{
//Not supported
}
public void Show()
{
_keyboardHelper.ActivateAutoShowKeybord();
}
public void BeginMoveDrag()
{
//Not supported
}
public void BeginResizeDrag(WindowEdge edge)
{
//Not supported
}
public Point Position
{
get { return _position; }
get { return _padding; }
set
{
_position = value;
PositionChanged?.Invoke(_position);
_padding = value;
SetNeedsLayout();
}
}
public Size MaxClientSize => Bounds.Size.ToAvalonia();
public IEnumerable<object> Surfaces => new object[] { this };
public void SetTitle(string title)
{
//Not supported
}
public IDisposable ShowDialog()
public AvaloniaView()
{
//Not supported
return Disposable.Empty;
}
public void Hide()
{
//Not supported
}
public void SetSystemDecorations(bool enabled)
{
//Not supported
}
public override void TouchesEnded(NSSet touches, UIEvent evt)
{
var touch = touches.AnyObject as UITouch;
if (touch != null)
{
var location = touch.LocationInView(this).ToAvalonia();
Input?.Invoke(new RawMouseEventArgs(
iOSPlatform.MouseDevice,
(uint)touch.Timestamp,
_inputRoot,
RawMouseEventType.LeftButtonUp,
location,
InputModifiers.None));
}
}
Point _touchLastPoint;
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
var touch = touches.AnyObject as UITouch;
if (touch != null)
{
var location = touch.LocationInView(this).ToAvalonia();
_touchLastPoint = location;
Input?.Invoke(new RawMouseEventArgs(iOSPlatform.MouseDevice, (uint)touch.Timestamp, _inputRoot,
RawMouseEventType.Move, location, InputModifiers.None));
Input?.Invoke(new RawMouseEventArgs(iOSPlatform.MouseDevice, (uint)touch.Timestamp, _inputRoot,
RawMouseEventType.LeftButtonDown, location, InputModifiers.None));
}
}
public override void TouchesMoved(NSSet touches, UIEvent evt)
{
var touch = touches.AnyObject as UITouch;
if (touch != null)
{
var location = touch.LocationInView(this).ToAvalonia();
if (iOSPlatform.MouseDevice.Captured != null)
Input?.Invoke(new RawMouseEventArgs(iOSPlatform.MouseDevice, (uint)touch.Timestamp, _inputRoot,
RawMouseEventType.Move, location, InputModifiers.LeftMouseButton));
else
{
//magic number based on test - correction of 0.02 is working perfect
double correction = 0.02;
Input?.Invoke(new RawMouseWheelEventArgs(iOSPlatform.MouseDevice, (uint)touch.Timestamp,
_inputRoot, location, (location - _touchLastPoint) * correction, InputModifiers.LeftMouseButton));
}
_touchLastPoint = location;
}
_impl = new EmbeddableImpl();
AddSubview(_impl);
BackgroundColor = UIColor.White;
AutoresizingMask = UIViewAutoresizing.All;
_root = new EmbeddableControlRoot(_impl);
_root.Prepare();
}
public void SetIcon(IWindowIconImpl icon)
public override void LayoutSubviews()
{
_impl.Frame = new CGRect(Padding.Left, Padding.Top,
Frame.Width - Padding.Left - Padding.Right,
Frame.Height - Padding.Top - Padding.Bottom);
}
}
class AvaloniaViewController : UIViewController
{
public AvaloniaView AvaloniaView { get; }
public AvaloniaViewController(UIWindow window)
{
AvaloniaView = new AvaloniaView(window, this);
}
public override void LoadView()
public object Content
{
View = AvaloniaView;
get { return _root.Content; }
set { _root.Content = value; }
}
}
}

31
src/iOS/Avalonia.iOS/AvaloniaWindow.cs

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Avalonia.Media;
using Foundation;
using UIKit;
namespace Avalonia.iOS
{
public sealed class AvaloniaWindow : UIWindow
{
readonly AvaloniaRootViewController _controller = new AvaloniaRootViewController();
public object Content
{
get { return _controller.Content; }
set { _controller.Content = value; }
}
public AvaloniaWindow() : base(UIScreen.MainScreen.Bounds)
{
RootViewController = _controller;
}
public Color StatusBarColor
{
get { return _controller.StatusBarColor; }
set { _controller.StatusBarColor = value; }
}
}
}

35
src/iOS/Avalonia.iOS/DisplayLinkRenderLoop.cs

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Avalonia.Rendering;
using CoreAnimation;
using Foundation;
using UIKit;
namespace Avalonia.iOS
{
class DisplayLinkRenderLoop : IRenderLoop
{
public event EventHandler<EventArgs> Tick;
private CADisplayLink _link;
public DisplayLinkRenderLoop()
{
_link = CADisplayLink.Create(OnFrame);
_link.AddToRunLoop(NSRunLoop.Main, NSRunLoop.NSDefaultRunLoopMode);
}
private void OnFrame()
{
try
{
Tick?.Invoke(this, new EventArgs());
}
catch (Exception e)
{
//TODO: log
}
}
}
}

28
src/iOS/Avalonia.iOS/EmbeddableImpl.cs

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Text;
using Avalonia.Platform;
using UIKit;
namespace Avalonia.iOS
{
class EmbeddableImpl : TopLevelImpl, IEmbeddableWindowImpl
{
public void SetTitle(string title)
{
}
public IDisposable ShowDialog()
{
return Disposable.Empty;
}
public void SetSystemDecorations(bool enabled)
{
}
public event Action LostFocus;
}
}

59
src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Avalonia.Controls.Platform.Surfaces;
using CoreGraphics;
using UIKit;
namespace Avalonia.iOS
{
/// <summary>
/// This is a bit weird, but CG doesn't provide proper bitmap
/// with lockable bits, but can create one from data pointer
/// So we are using our own buffer here.
/// </summary>
class EmulatedFramebuffer : ILockedFramebuffer
{
public EmulatedFramebuffer(UIView view)
{
var factor = (int) UIScreen.MainScreen.Scale;
var frame = view.Frame;
Width = (int) frame.Width * factor;
Height = (int) frame.Height * factor;
RowBytes = Width * 4;
Dpi = new Size(96, 96) * factor;
Format = PixelFormat.Rgba8888;
Address = Marshal.AllocHGlobal(Height * RowBytes);
}
public void Dispose()
{
if (Address == IntPtr.Zero)
return;
var nfo = (int) CGBitmapFlags.ByteOrder32Big | (int) CGImageAlphaInfo.PremultipliedLast;
using (var colorSpace = CGColorSpace.CreateDeviceRGB())
using (var bContext = new CGBitmapContext(Address, Width, Height, 8, Width * 4,
colorSpace, (CGImageAlphaInfo) nfo))
using (var image = bContext.ToImage())
using (var context = UIGraphics.GetCurrentContext())
{
// flip the image for CGContext.DrawImage
context.TranslateCTM(0, Height);
context.ScaleCTM(1, -1);
context.DrawImage(new CGRect(0, 0, Width, Height), image);
}
Marshal.FreeHGlobal(Address);
Address = IntPtr.Zero;
}
public IntPtr Address { get; private set; }
public int Width { get; }
public int Height { get; }
public int RowBytes { get; }
public Size Dpi { get; }
public PixelFormat Format { get; }
}
}

10
src/iOS/Avalonia.iOS/Extensions.cs

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Media;
using CoreGraphics;
using UIKit;
namespace Avalonia.iOS
{
@ -11,5 +13,13 @@ namespace Avalonia.iOS
public static Size ToAvalonia(this CGSize size) => new Size(size.Width, size.Height);
public static Point ToAvalonia(this CGPoint point) => new Point(point.X, point.Y);
static nfloat ColorComponent(byte c) => ((float) c) / 255;
public static UIColor ToUiColor(this Color color)=>new UIColor(
ColorComponent(color.R),
ColorComponent(color.G),
ColorComponent(color.B),
ColorComponent(color.A));
}
}

106
src/iOS/Avalonia.iOS/PlatformThreadingInterface.cs

@ -14,91 +14,59 @@ namespace Avalonia.iOS
{
class PlatformThreadingInterface : IPlatformThreadingInterface
{
static Stopwatch St = Stopwatch.StartNew();
class Timer
private bool _signaled;
public static PlatformThreadingInterface Instance { get; } = new PlatformThreadingInterface();
public bool CurrentThreadIsLoopThread => NSThread.Current.IsMainThread;
public event Action Signaled;
public void RunLoop(CancellationToken cancellationToken)
{
readonly Action _tick;
readonly TimeSpan _interval;
TimeSpan _nextTick;
//Mobile platforms are using external main loop
throw new NotSupportedException();
}
/*
class Timer : NSObject
{
private readonly Action _tick;
private NSTimer _timer;
public Timer(Action tick, TimeSpan interval)
public Timer(TimeSpan interval, Action tick)
{
_tick = tick;
_interval = interval;
_nextTick = St.Elapsed + _interval;
_timer = new NSTimer(NSDate.Now, interval.TotalSeconds, true, OnTick);
}
public void Tick(TimeSpan now)
[Export("onTick")]
private void OnTick(NSTimer nsTimer)
{
if (now > _nextTick)
{
_nextTick = now + _interval;
_tick();
}
_tick();
}
}
readonly List<Timer> _timers = new List<Timer>();
bool _signaled;
readonly object _lock = new object();
private CADisplayLink _link;
public Action Render { get; set; }
PlatformThreadingInterface()
{
// For some reason it doesn't work when I specify OnFrame method directly
// ReSharper disable once ConvertClosureToMethodGroup
(_link = CADisplayLink.Create(() => OnFrame())).AddToRunLoop(NSRunLoop.Main, NSRunLoop.NSDefaultRunLoopMode);
}
private void OnFrame()
{
var now = St.Elapsed;
List<Timer> timers;
lock (_lock)
timers = _timers.ToList();
foreach (var timer in timers)
timer.Tick(now);
do
protected override void Dispose(bool disposing)
{
lock (_lock)
if (!_signaled)
break;
else
_signaled = false;
Signaled?.Invoke();
} while (false);
Render?.Invoke();
}
public void RunLoop(CancellationToken cancellationToken)
{
}
if(disposing)
_timer.Dispose();
base.Dispose(disposing);
}
}*/
public IDisposable StartTimer(TimeSpan interval, Action tick)
{
lock (_lock)
{
var timer = new Timer(tick, interval);
_timers.Add(timer);
return Disposable.Create(() =>
{
lock (_lock) _timers.Remove(timer);
});
}
}
=> NSTimer.CreateRepeatingScheduledTimer(interval, _ => tick());
public void Signal()
{
lock (_lock)
lock (this)
{
if(_signaled)
return;
_signaled = true;
}
NSRunLoop.Main.BeginInvokeOnMainThread(() =>
{
lock (this)
_signaled = false;
Signaled?.Invoke();
});
}
public bool CurrentThreadIsLoopThread => NSThread.Current.IsMainThread;
public static PlatformThreadingInterface Instance { get; } = new PlatformThreadingInterface();
public event Action Signaled;
}
}

2
src/iOS/Avalonia.iOS/Specific/KeyboardEventsHelper.cs

@ -37,7 +37,7 @@ namespace Avalonia.iOS.Specific
/// view.ResignFirstResponder();
/// </summary>
/// <typeparam name="TView">View that needs keyboard events and show/hide keyboard</typeparam>
internal class KeyboardEventsHelper<TView> where TView : UIView, IWindowImpl
internal class KeyboardEventsHelper<TView> where TView : UIView, ITopLevelImpl
{
private TView _view;
private IInputElement _lastFocusedElement;

192
src/iOS/Avalonia.iOS/TopLevelImpl.cs

@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Disposables;
using System.Text;
using CoreAnimation;
using CoreGraphics;
using Foundation;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Skia.iOS;
using UIKit;
using Avalonia.iOS.Specific;
using ObjCRuntime;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
namespace Avalonia.iOS
{
[Adopts("UIKeyInput")]
class TopLevelImpl : UIView, ITopLevelImpl, IFramebufferPlatformSurface
{
private IInputRoot _inputRoot;
private readonly KeyboardEventsHelper<TopLevelImpl> _keyboardHelper;
private Point _position;
public TopLevelImpl()
{
_keyboardHelper = new KeyboardEventsHelper<TopLevelImpl>(this);
AutoresizingMask = UIViewAutoresizing.All;
}
[Export("hasText")]
public bool HasText => _keyboardHelper.HasText();
[Export("insertText:")]
public void InsertText(string text) => _keyboardHelper.InsertText(text);
[Export("deleteBackward")]
public void DeleteBackward() => _keyboardHelper.DeleteBackward();
public override bool CanBecomeFirstResponder => _keyboardHelper.CanBecomeFirstResponder();
public Action Activated { get; set; }
public Action Closed { get; set; }
public Action Deactivated { get; set; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action<double> ScalingChanged { get; set; }
public Action<Point> PositionChanged { get; set; }
public IPlatformHandle Handle => null;
public double Scaling => UIScreen.MainScreen.Scale;
public WindowState WindowState
{
get { return WindowState.Normal; }
set { }
}
public override void LayoutSubviews() => Resized?.Invoke(ClientSize);
public Size ClientSize
{
get { return Bounds.Size.ToAvalonia(); }
set { InvokeOnMainThread(() => Resized?.Invoke(ClientSize)); }
}
public void Activate()
{
}
public override void Draw(CGRect rect)
{
Paint?.Invoke(new Rect(rect.X, rect.Y, rect.Width, rect.Height));
}
public void Invalidate(Rect rect) => SetNeedsDisplay();
public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
public Point PointToClient(Point point) => point;
public Point PointToScreen(Point point) => point;
public void SetCursor(IPlatformHandle cursor)
{
//Not supported
}
public void Show()
{
_keyboardHelper.ActivateAutoShowKeybord();
}
public void BeginMoveDrag()
{
//Not supported
}
public void BeginResizeDrag(WindowEdge edge)
{
//Not supported
}
public Point Position
{
get { return _position; }
set
{
_position = value;
PositionChanged?.Invoke(_position);
}
}
public Size MaxClientSize => Bounds.Size.ToAvalonia();
public IEnumerable<object> Surfaces => new object[] { this };
public void Hide()
{
//Not supported
}
public override void TouchesEnded(NSSet touches, UIEvent evt)
{
var touch = touches.AnyObject as UITouch;
if (touch != null)
{
var location = touch.LocationInView(this).ToAvalonia();
Input?.Invoke(new RawMouseEventArgs(
iOSPlatform.MouseDevice,
(uint)touch.Timestamp,
_inputRoot,
RawMouseEventType.LeftButtonUp,
location,
InputModifiers.None));
}
}
Point _touchLastPoint;
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
var touch = touches.AnyObject as UITouch;
if (touch != null)
{
var location = touch.LocationInView(this).ToAvalonia();
_touchLastPoint = location;
Input?.Invoke(new RawMouseEventArgs(iOSPlatform.MouseDevice, (uint)touch.Timestamp, _inputRoot,
RawMouseEventType.Move, location, InputModifiers.None));
Input?.Invoke(new RawMouseEventArgs(iOSPlatform.MouseDevice, (uint)touch.Timestamp, _inputRoot,
RawMouseEventType.LeftButtonDown, location, InputModifiers.None));
}
}
public override void TouchesMoved(NSSet touches, UIEvent evt)
{
var touch = touches.AnyObject as UITouch;
if (touch != null)
{
var location = touch.LocationInView(this).ToAvalonia();
if (iOSPlatform.MouseDevice.Captured != null)
Input?.Invoke(new RawMouseEventArgs(iOSPlatform.MouseDevice, (uint)touch.Timestamp, _inputRoot,
RawMouseEventType.Move, location, InputModifiers.LeftMouseButton));
else
{
//magic number based on test - correction of 0.02 is working perfect
double correction = 0.02;
Input?.Invoke(new RawMouseWheelEventArgs(iOSPlatform.MouseDevice, (uint)touch.Timestamp,
_inputRoot, location, (location - _touchLastPoint) * correction, InputModifiers.LeftMouseButton));
}
_touchLastPoint = location;
}
}
public void SetIcon(IWindowIconImpl icon)
{
}
public ILockedFramebuffer Lock() => new EmulatedFramebuffer(this);
}
}

14
src/iOS/Avalonia.iOS/WindowingPlatformImpl.cs

@ -3,26 +3,16 @@ using System;
namespace Avalonia.iOS
{
// This is somewhat generic, could probably put this elsewhere. But I don't think
// it should part of the iOS App Delegate
//
class WindowingPlatformImpl : IWindowingPlatform
{
private readonly IWindowImpl _window;
public WindowingPlatformImpl(IWindowImpl window)
{
_window = window;
}
public IWindowImpl CreateWindow()
{
return _window;
throw new NotSupportedException();
}
public IEmbeddableWindowImpl CreateEmbeddableWindow()
{
throw new NotImplementedException();
throw new NotSupportedException();
}
public IPopupImpl CreatePopup()

14
src/iOS/Avalonia.iOS/iOSPlatform.cs

@ -8,6 +8,7 @@ using Avalonia.Shared.PlatformSupport;
using Avalonia.Skia;
using UIKit;
using Avalonia.Controls;
using Avalonia.Rendering;
namespace Avalonia
{
@ -18,7 +19,7 @@ namespace Avalonia
builder.UseWindowingSubsystem(iOSPlatform.Initialize, "iOS");
return builder;
}
/*
// TODO: Can we merge this with UseSkia somehow once HW/platform cleanup is done?
public static T UseSkiaViewHost<T>(this T builder) where T : AppBuilderBase<T>, new()
{
@ -33,16 +34,13 @@ namespace Avalonia
SkiaPlatform.Initialize();
return builder;
}
}*/
}
}
namespace Avalonia.iOS
{
// TODO: Perhaps we should make this class handle all these interfaces directly, like we
// do for Win32 and Gtk platforms
//
public class iOSPlatform //: IPlatformThreadingInterface, IPlatformSettings, IWindowingPlatform
public class iOSPlatform
{
internal static MouseDevice MouseDevice;
internal static KeyboardDevice KeyboardDevice;
@ -62,7 +60,9 @@ namespace Avalonia.iOS
.Bind<IMouseDevice>().ToConstant(MouseDevice)
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IPlatformThreadingInterface>().ToConstant(PlatformThreadingInterface.Instance)
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>();
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()
.Bind<IWindowingPlatform>().ToSingleton<WindowingPlatformImpl>()
.Bind<IRenderLoop>().ToSingleton<DisplayLinkRenderLoop>();
}
}
}

16
src/iOS/Avalonia.iOSTestApplication/AppDelegate.cs

@ -7,7 +7,6 @@ using Avalonia.Controls;
using Avalonia.iOS;
using Avalonia.Media;
using Avalonia.Threading;
using TestApplication;
using UIKit;
namespace Avalonia.iOSTestApplication
@ -18,6 +17,8 @@ namespace Avalonia.iOSTestApplication
[Register("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
public override UIWindow Window { get; set; }
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
@ -27,16 +28,9 @@ namespace Avalonia.iOSTestApplication
//
public override bool FinishedLaunching(UIApplication uiapp, NSDictionary options)
{
var app = new App();
AppBuilder.Configure(app)
.UseiOS()
.UseSkiaViewHost()
.UseSkia()
.SetupWithoutStarting();
app.Run();
AppBuilder.Configure<SimpleApp>().UseSkia().UseiOS().SetupWithoutStarting();
Window = new AvaloniaWindow { Content = new SimpleControl()};
Window.MakeKeyAndVisible();
return true;
}
}

6
src/iOS/Avalonia.iOSTestApplication/Avalonia.iOSTestApplication.csproj

@ -97,6 +97,8 @@
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="SimpleApp.xaml.cs" />
<Compile Include="SimpleControl.cs" />
<None Include="GettingStarted.Xamarin" />
<None Include="Info.plist" />
<Compile Include="Properties\AssemblyInfo.cs" />
@ -196,5 +198,9 @@
<IsWatchApp>false</IsWatchApp>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="SimpleApp.xaml">
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project>

6
src/iOS/Avalonia.iOSTestApplication/SimpleApp.xaml

@ -0,0 +1,6 @@
<Application xmlns="https://github.com/avaloniaui">
<Application.Styles>
<StyleInclude Source="resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default"/>
</Application.Styles>
</Application>

20
src/iOS/Avalonia.iOSTestApplication/SimpleApp.xaml.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Avalonia.Markup.Xaml;
using Foundation;
using UIKit;
namespace Avalonia.iOSTestApplication
{
public class SimpleApp : Avalonia.Application
{
public override void Initialize()
{
//Enforce load
new Avalonia.Themes.Default.DefaultTheme();
AvaloniaXamlLoader.Load(this);
}
}
}

20
src/iOS/Avalonia.iOSTestApplication/SimpleControl.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Avalonia.Controls;
using Avalonia.Media;
namespace Avalonia.iOSTestApplication
{
class SimpleControl : ContentControl
{
public SimpleControl()
{
Content = new Button() {Content = "WAT"};
MinWidth = 100;
MinHeight = 200;
Background = Brushes.CadetBlue;
}
}
}
Loading…
Cancel
Save