diff --git a/Avalonia.sln b/Avalonia.sln index 4e897b86c7..3ab8048857 100644 --- a/Avalonia.sln +++ b/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,7 +176,6 @@ 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 @@ -1486,34 +1483,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 +2471,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} diff --git a/samples/ControlCatalog.iOS/AppDelegate.cs b/samples/ControlCatalog.iOS/AppDelegate.cs index 1871facf55..a8fc6b30a0 100644 --- a/samples/ControlCatalog.iOS/AppDelegate.cs +++ b/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() .UseiOS() - .UseSkiaViewHost() - .UseSkia() - .Start(); - + .UseSkia().SetupWithoutStarting(); + Window = new AvaloniaWindow() {Content = new MainView(), StatusBarColor = Colors.LightSteelBlue}; + Window.MakeKeyAndVisible(); return true; } } diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj index 1c4d31b62c..d0c73b2553 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj +++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj @@ -20,8 +20,21 @@ 4 false i386 - None - true + SdkOnly + True + 9.1 + False + False + False + False + False + False + False + False + True + Default + HttpClientHandler + False none diff --git a/src/Shared/PlatformSupport/AssetLoader.cs b/src/Shared/PlatformSupport/AssetLoader.cs index 8da663f554..bbf19f731d 100644 --- a/src/Shared/PlatformSupport/AssetLoader.cs +++ b/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 } diff --git a/src/Skia/Avalonia.Skia.Android/AndroidPlatformRenderInterface.cs b/src/Skia/Avalonia.Skia.Android/AndroidPlatformRenderInterface.cs deleted file mode 100644 index be02111cbd..0000000000 --- a/src/Skia/Avalonia.Skia.Android/AndroidPlatformRenderInterface.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Android.App; -using Android.Content; -using Android.OS; -using Android.Runtime; -using Android.Views; -using Android.Widget; -using Avalonia.Controls.Platform.Surfaces; -using Avalonia.Platform; - -namespace Avalonia.Skia -{ - partial class PlatformRenderInterface - { - public IRenderTarget CreateRenderTarget(IEnumerable surfaces) - { - var fb = surfaces?.OfType().FirstOrDefault(); - if (fb == null) - throw new ArgumentException("Avalonia.Skia.Android is only capable of drawing on framebuffer"); - return new FramebufferRenderTarget(fb); - } - } -} \ No newline at end of file diff --git a/src/Skia/Avalonia.Skia.Android/Avalonia.Skia.Android.csproj b/src/Skia/Avalonia.Skia.Android/Avalonia.Skia.Android.csproj index ef950fe786..c644bd84b3 100644 --- a/src/Skia/Avalonia.Skia.Android/Avalonia.Skia.Android.csproj +++ b/src/Skia/Avalonia.Skia.Android/Avalonia.Skia.Android.csproj @@ -85,9 +85,6 @@ Avalonia.Styling - - - diff --git a/src/Skia/Avalonia.Skia.Desktop.NetStandard/Avalonia.Skia.Desktop.NetStandard.csproj b/src/Skia/Avalonia.Skia.Desktop.NetStandard/Avalonia.Skia.Desktop.NetStandard.csproj index fde3375c43..f421ed04f4 100644 --- a/src/Skia/Avalonia.Skia.Desktop.NetStandard/Avalonia.Skia.Desktop.NetStandard.csproj +++ b/src/Skia/Avalonia.Skia.Desktop.NetStandard/Avalonia.Skia.Desktop.NetStandard.csproj @@ -44,9 +44,6 @@ Properties\SharedAssemblyInfo.cs - - PlatformRenderInterfaceDesktop.cs - diff --git a/src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj b/src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj index c07db0908f..74d592f9bf 100644 --- a/src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj +++ b/src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj @@ -71,7 +71,6 @@ - diff --git a/src/Skia/Avalonia.Skia.Desktop/PlatformRenderInterfaceDesktop.cs b/src/Skia/Avalonia.Skia.Desktop/PlatformRenderInterfaceDesktop.cs deleted file mode 100644 index 9382a4f6e2..0000000000 --- a/src/Skia/Avalonia.Skia.Desktop/PlatformRenderInterfaceDesktop.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Avalonia.Controls.Platform.Surfaces; -using Avalonia.Platform; - -namespace Avalonia.Skia -{ - partial class PlatformRenderInterface - { - public IRenderTarget CreateRenderTarget(IEnumerable surfaces) - { - var fb = surfaces?.OfType().FirstOrDefault(); - if (fb == null) - throw new Exception("Avalonia.Skia.Deskop currently only supports framebuffer render target"); - return new FramebufferRenderTarget(fb); - } - } -} diff --git a/src/Skia/Avalonia.Skia.iOS.TestApp/AppDelegate.cs b/src/Skia/Avalonia.Skia.iOS.TestApp/AppDelegate.cs deleted file mode 100644 index b40a253c99..0000000000 --- a/src/Skia/Avalonia.Skia.iOS.TestApp/AppDelegate.cs +++ /dev/null @@ -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. - } - } -} - - diff --git a/src/Skia/Avalonia.Skia.iOS.TestApp/Avalonia.Skia.iOS.TestApp.csproj b/src/Skia/Avalonia.Skia.iOS.TestApp/Avalonia.Skia.iOS.TestApp.csproj deleted file mode 100644 index 9cef634048..0000000000 --- a/src/Skia/Avalonia.Skia.iOS.TestApp/Avalonia.Skia.iOS.TestApp.csproj +++ /dev/null @@ -1,143 +0,0 @@ - - - - Debug - iPhoneSimulator - {DA49C5F3-BE95-461C-B999-072128CCF59E} - {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Exe - Avalonia.Skia.iOS.TestApp - Resources - AvaloniaSkiaiOSTestApp - - - true - full - false - bin\iPhoneSimulator\Debug - DEBUG - prompt - 4 - false - i386 - SdkOnly - true - - - none - true - bin\iPhoneSimulator\Release - prompt - 4 - None - i386 - false - - - true - full - false - bin\iPhone\Debug - DEBUG - prompt - 4 - false - ARMv7, ARM64 - Entitlements.plist - iPhone Developer - true - - - - - - - none - true - bin\iPhone\Release - prompt - 4 - Entitlements.plist - ARMv7, ARM64 - false - iPhone Developer - - - none - True - bin\iPhone\Ad-Hoc - prompt - 4 - False - ARMv7, ARM64 - Entitlements.plist - True - Automatic:AdHoc - iPhone Distribution - - - none - True - bin\iPhone\AppStore - prompt - 4 - False - ARMv7, ARM64 - Entitlements.plist - Automatic:AppStore - iPhone Distribution - - - - - - - - - - - - - - - - - - - - {d211e587-d8bc-45b9-95a4-f297c8fa5200} - Avalonia.Animation - - - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} - Avalonia.Base - - - {d2221c82-4a25-4583-9b43-d791e3f6820c} - Avalonia.Controls - - - {62024b2d-53eb-4638-b26b-85eeaa54866e} - Avalonia.Input - - - {42472427-4774-4c81-8aff-9f27b8e31721} - Avalonia.Layout - - - {eb582467-6abb-43a1-b052-e981ba910e3a} - Avalonia.Visuals - - - {f1baa01a-f176-4c6a-b39d-5b40bb1b148f} - Avalonia.Styling - - - {47be08a7-5985-410b-9ffc-2264b8ea595f} - Avalonia.Skia.iOS - false - false - - - - \ No newline at end of file diff --git a/src/Skia/Avalonia.Skia.iOS.TestApp/Avalonia.Skia.iOS.TestApp.v2.ncrunchproject b/src/Skia/Avalonia.Skia.iOS.TestApp/Avalonia.Skia.iOS.TestApp.v2.ncrunchproject deleted file mode 100644 index f744eecae0..0000000000 --- a/src/Skia/Avalonia.Skia.iOS.TestApp/Avalonia.Skia.iOS.TestApp.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Skia/Avalonia.Skia.iOS.TestApp/Entitlements.plist b/src/Skia/Avalonia.Skia.iOS.TestApp/Entitlements.plist deleted file mode 100644 index 0c67376eba..0000000000 --- a/src/Skia/Avalonia.Skia.iOS.TestApp/Entitlements.plist +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/Skia/Avalonia.Skia.iOS.TestApp/GettingStarted.Xamarin b/src/Skia/Avalonia.Skia.iOS.TestApp/GettingStarted.Xamarin deleted file mode 100644 index 810f716685..0000000000 --- a/src/Skia/Avalonia.Skia.iOS.TestApp/GettingStarted.Xamarin +++ /dev/null @@ -1,4 +0,0 @@ - - GS\iOS\CS\iOSApp\GettingStarted.html - false - \ No newline at end of file diff --git a/src/Skia/Avalonia.Skia.iOS.TestApp/Info.plist b/src/Skia/Avalonia.Skia.iOS.TestApp/Info.plist deleted file mode 100644 index ca8cbf88f9..0000000000 --- a/src/Skia/Avalonia.Skia.iOS.TestApp/Info.plist +++ /dev/null @@ -1,42 +0,0 @@ - - - - - CFBundleDisplayName - Avalonia.Skia.iOS.TestApp - CFBundleIdentifier - com.companyname.Avalonia.Skia.iOS.TestApp - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1.0 - LSRequiresIPhoneOS - - MinimumOSVersion - 8.0 - UIDeviceFamily - - 1 - 2 - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/src/Skia/Avalonia.Skia.iOS.TestApp/Main.cs b/src/Skia/Avalonia.Skia.iOS.TestApp/Main.cs deleted file mode 100644 index 3f73331c9e..0000000000 --- a/src/Skia/Avalonia.Skia.iOS.TestApp/Main.cs +++ /dev/null @@ -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"); - } - } -} \ No newline at end of file diff --git a/src/Skia/Avalonia.Skia.iOS.TestApp/MainView.cs b/src/Skia/Avalonia.Skia.iOS.TestApp/MainView.cs deleted file mode 100644 index c27f54b277..0000000000 --- a/src/Skia/Avalonia.Skia.iOS.TestApp/MainView.cs +++ /dev/null @@ -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() - .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(); - } - } -} \ No newline at end of file diff --git a/src/Skia/Avalonia.Skia.iOS.TestApp/Properties/AssemblyInfo.cs b/src/Skia/Avalonia.Skia.iOS.TestApp/Properties/AssemblyInfo.cs deleted file mode 100644 index 666846c18a..0000000000 --- a/src/Skia/Avalonia.Skia.iOS.TestApp/Properties/AssemblyInfo.cs +++ /dev/null @@ -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")] diff --git a/src/Skia/Avalonia.Skia.iOS.TestApp/Resources/LaunchScreen.xib b/src/Skia/Avalonia.Skia.iOS.TestApp/Resources/LaunchScreen.xib deleted file mode 100644 index 3f72070c91..0000000000 --- a/src/Skia/Avalonia.Skia.iOS.TestApp/Resources/LaunchScreen.xib +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj b/src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj index 0b1f8c9b62..08fcd61471 100644 --- a/src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj +++ b/src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj @@ -37,7 +37,6 @@ true - diff --git a/src/Skia/Avalonia.Skia.iOS/PlatformRenderingInterfaceIos.cs b/src/Skia/Avalonia.Skia.iOS/PlatformRenderingInterfaceIos.cs deleted file mode 100644 index fdc7389800..0000000000 --- a/src/Skia/Avalonia.Skia.iOS/PlatformRenderingInterfaceIos.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Avalonia.Platform; -using Foundation; -using UIKit; - -namespace Avalonia.Skia -{ - partial class PlatformRenderInterface - { - public IRenderTarget CreateRenderTarget(IEnumerable surfaces) - { - return new WindowRenderTarget(); - } - } -} \ No newline at end of file diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 4cfe1e3e40..f0735bb0df 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using Avalonia.Controls.Platform.Surfaces; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering; @@ -64,5 +66,13 @@ namespace Avalonia.Skia return new BitmapImpl(width, height); } + + public virtual IRenderTarget CreateRenderTarget(IEnumerable surfaces) + { + var fb = surfaces?.OfType().FirstOrDefault(); + if (fb == null) + throw new Exception("Skia backend currently only supports framebuffer render target"); + return new FramebufferRenderTarget(fb); + } } } diff --git a/src/iOS/Avalonia.iOS/AppBuilder.cs b/src/iOS/Avalonia.iOS/AppBuilder.cs new file mode 100644 index 0000000000..06ac7ed6cd --- /dev/null +++ b/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 + { + public AppBuilder() : base(new StandardRuntimePlatform(), + builder => StandardRuntimePlatformServices.Register(builder.Instance?.GetType().Assembly)) + { + + } + } +} \ No newline at end of file diff --git a/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj b/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj index cbe17022b1..d7cba35d29 100644 --- a/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj +++ b/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj @@ -23,6 +23,7 @@ false true iPhone Developer + true none @@ -32,13 +33,21 @@ 4 false iPhone Developer + true + + + + + + + - + diff --git a/src/iOS/Avalonia.iOS/AvaloniaRootViewController.cs b/src/iOS/Avalonia.iOS/AvaloniaRootViewController.cs new file mode 100644 index 0000000000..52f55d6574 --- /dev/null +++ b/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(); + } + } +} \ No newline at end of file diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs index f9df1fac5e..d70a31a706 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.cs +++ b/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 _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(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 Input { get; set; } - public Action Paint { get; set; } - public Action Resized { get; set; } - public Action ScalingChanged { get; set; } - public Action 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 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; } } } } diff --git a/src/iOS/Avalonia.iOS/AvaloniaWindow.cs b/src/iOS/Avalonia.iOS/AvaloniaWindow.cs new file mode 100644 index 0000000000..d3f88e0e1e --- /dev/null +++ b/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; } + } + } +} \ No newline at end of file diff --git a/src/iOS/Avalonia.iOS/DisplayLinkRenderLoop.cs b/src/iOS/Avalonia.iOS/DisplayLinkRenderLoop.cs new file mode 100644 index 0000000000..ef045f61bf --- /dev/null +++ b/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 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 + } + } + } +} \ No newline at end of file diff --git a/src/iOS/Avalonia.iOS/EmbeddableImpl.cs b/src/iOS/Avalonia.iOS/EmbeddableImpl.cs new file mode 100644 index 0000000000..50a3cd9ec0 --- /dev/null +++ b/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; + } +} diff --git a/src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs b/src/iOS/Avalonia.iOS/EmulatedFramebuffer.cs new file mode 100644 index 0000000000..1baed80ed7 --- /dev/null +++ b/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 +{ + + /// + /// 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. + /// + 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; } + } +} diff --git a/src/iOS/Avalonia.iOS/Extensions.cs b/src/iOS/Avalonia.iOS/Extensions.cs index d2d7c1cfe2..5364129911 100644 --- a/src/iOS/Avalonia.iOS/Extensions.cs +++ b/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)); } } diff --git a/src/iOS/Avalonia.iOS/PlatformThreadingInterface.cs b/src/iOS/Avalonia.iOS/PlatformThreadingInterface.cs index 4831c23855..e2e72f4c3c 100644 --- a/src/iOS/Avalonia.iOS/PlatformThreadingInterface.cs +++ b/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 _timers = new List(); - 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 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; } } diff --git a/src/iOS/Avalonia.iOS/Specific/KeyboardEventsHelper.cs b/src/iOS/Avalonia.iOS/Specific/KeyboardEventsHelper.cs index 80c6fc5f49..567bb9a5e0 100644 --- a/src/iOS/Avalonia.iOS/Specific/KeyboardEventsHelper.cs +++ b/src/iOS/Avalonia.iOS/Specific/KeyboardEventsHelper.cs @@ -37,7 +37,7 @@ namespace Avalonia.iOS.Specific /// view.ResignFirstResponder(); /// /// View that needs keyboard events and show/hide keyboard - internal class KeyboardEventsHelper where TView : UIView, IWindowImpl + internal class KeyboardEventsHelper where TView : UIView, ITopLevelImpl { private TView _view; private IInputElement _lastFocusedElement; diff --git a/src/iOS/Avalonia.iOS/TopLevelImpl.cs b/src/iOS/Avalonia.iOS/TopLevelImpl.cs new file mode 100644 index 0000000000..96ab2786f7 --- /dev/null +++ b/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 _keyboardHelper; + private Point _position; + + public TopLevelImpl() + { + _keyboardHelper = new KeyboardEventsHelper(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 Input { get; set; } + public Action Paint { get; set; } + public Action Resized { get; set; } + public Action ScalingChanged { get; set; } + public Action 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 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); + } +} diff --git a/src/iOS/Avalonia.iOS/WindowingPlatformImpl.cs b/src/iOS/Avalonia.iOS/WindowingPlatformImpl.cs index b6a683e10a..5cac259ce7 100644 --- a/src/iOS/Avalonia.iOS/WindowingPlatformImpl.cs +++ b/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() diff --git a/src/iOS/Avalonia.iOS/iOSPlatform.cs b/src/iOS/Avalonia.iOS/iOSPlatform.cs index 1b69735221..165b7d44f9 100644 --- a/src/iOS/Avalonia.iOS/iOSPlatform.cs +++ b/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(this T builder) where T : AppBuilderBase, 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().ToConstant(MouseDevice) .Bind().ToSingleton() .Bind().ToConstant(PlatformThreadingInterface.Instance) - .Bind().ToSingleton(); + .Bind().ToSingleton() + .Bind().ToSingleton() + .Bind().ToSingleton(); } } } diff --git a/src/iOS/Avalonia.iOSTestApplication/AppDelegate.cs b/src/iOS/Avalonia.iOSTestApplication/AppDelegate.cs index 211b7d5daa..4591fb2ae8 100644 --- a/src/iOS/Avalonia.iOSTestApplication/AppDelegate.cs +++ b/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().UseSkia().UseiOS().SetupWithoutStarting(); + Window = new AvaloniaWindow { Content = new SimpleControl()}; + Window.MakeKeyAndVisible(); return true; } } diff --git a/src/iOS/Avalonia.iOSTestApplication/Avalonia.iOSTestApplication.csproj b/src/iOS/Avalonia.iOSTestApplication/Avalonia.iOSTestApplication.csproj index 88123bf56c..ff7aa94502 100644 --- a/src/iOS/Avalonia.iOSTestApplication/Avalonia.iOSTestApplication.csproj +++ b/src/iOS/Avalonia.iOSTestApplication/Avalonia.iOSTestApplication.csproj @@ -97,6 +97,8 @@ + + @@ -196,5 +198,9 @@ false + + + + \ No newline at end of file diff --git a/src/iOS/Avalonia.iOSTestApplication/SimpleApp.xaml b/src/iOS/Avalonia.iOSTestApplication/SimpleApp.xaml new file mode 100644 index 0000000000..214bcd8797 --- /dev/null +++ b/src/iOS/Avalonia.iOSTestApplication/SimpleApp.xaml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/iOS/Avalonia.iOSTestApplication/SimpleApp.xaml.cs b/src/iOS/Avalonia.iOSTestApplication/SimpleApp.xaml.cs new file mode 100644 index 0000000000..b287755ef6 --- /dev/null +++ b/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); + } + } +} \ No newline at end of file diff --git a/src/iOS/Avalonia.iOSTestApplication/SimpleControl.cs b/src/iOS/Avalonia.iOSTestApplication/SimpleControl.cs new file mode 100644 index 0000000000..7e888f65bc --- /dev/null +++ b/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; + } + } +} \ No newline at end of file