Browse Source

Merge branch 'master' into master

Artyom 6 years ago
parent
commit
c7642e58fe
84 changed files with 2130 additions and 5907 deletions
  1. 0 29
      Avalonia.sln
  2. 23 5
      nukebuild/Build.cs
  3. 2 1
      nukebuild/_build.csproj
  4. 9 13
      samples/ControlCatalog.NetCore/Program.cs
  5. 11 0
      samples/ControlCatalog/App.xaml.cs
  6. 46 0
      src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs
  7. 19 54
      src/Avalonia.Controls/AppBuilderBase.cs
  8. 14 202
      src/Avalonia.Controls/Application.cs
  9. 133 0
      src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
  10. 8 3
      src/Avalonia.Controls/ApplicationLifetimes/ControlledApplicationLifetimeExitEventArgs.cs
  11. 7 0
      src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs
  12. 31 0
      src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
  13. 5 8
      src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs
  14. 7 0
      src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs
  15. 22 0
      src/Avalonia.Controls/ApplicationLifetimes/StartupEventArgs.cs
  16. 27 6
      src/Avalonia.Controls/ColumnDefinition.cs
  17. 67 0
      src/Avalonia.Controls/DesktopApplicationExtensions.cs
  18. 104 69
      src/Avalonia.Controls/DockPanel.cs
  19. 12 2
      src/Avalonia.Controls/ItemsControl.cs
  20. 12 1
      src/Avalonia.Controls/ListBox.cs
  21. 43 22
      src/Avalonia.Controls/Primitives/RangeBase.cs
  22. 409 235
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  23. 31 10
      src/Avalonia.Controls/RowDefinition.cs
  24. 0 36
      src/Avalonia.Controls/StartupEventArgs.cs
  25. 0 20
      src/Avalonia.Controls/TopLevel.cs
  26. 24 2
      src/Avalonia.Controls/TreeView.cs
  27. 18 42
      src/Avalonia.Controls/Window.cs
  28. 0 134
      src/Avalonia.Controls/WindowCollection.cs
  29. 4 6
      src/Avalonia.Controls/WrapPanel.cs
  30. 2 10
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  31. 9 1
      src/Avalonia.Styling/StyledElement.cs
  32. 3 7
      src/Avalonia.X11/X11KeyTransform.cs
  33. 13 2
      src/Avalonia.X11/X11Window.cs
  34. 0 11
      src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
  35. 0 51
      src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs
  36. 0 84
      src/Gtk/Avalonia.Gtk3/CursorFactory.cs
  37. 0 62
      src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
  38. 0 86
      src/Gtk/Avalonia.Gtk3/GdkCursor.cs
  39. 0 1341
      src/Gtk/Avalonia.Gtk3/GdkKey.cs
  40. 0 115
      src/Gtk/Avalonia.Gtk3/Gtk3ForeignX11SystemDialog.cs
  41. 0 168
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  42. 0 24
      src/Gtk/Avalonia.Gtk3/GtkScreen.cs
  43. 0 9
      src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs
  44. 0 144
      src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
  45. 0 20
      src/Gtk/Avalonia.Gtk3/Interop/CairoSurface.cs
  46. 0 30
      src/Gtk/Avalonia.Gtk3/Interop/GException.cs
  47. 0 87
      src/Gtk/Avalonia.Gtk3/Interop/GObject.cs
  48. 0 46
      src/Gtk/Avalonia.Gtk3/Interop/GlibPriority.cs
  49. 0 58
      src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs
  50. 0 12
      src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs
  51. 0 37
      src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs
  52. 0 790
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  53. 0 20
      src/Gtk/Avalonia.Gtk3/Interop/NativeException.cs
  54. 0 65
      src/Gtk/Avalonia.Gtk3/Interop/Pixbuf.cs
  55. 0 156
      src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs
  56. 0 47
      src/Gtk/Avalonia.Gtk3/Interop/Signal.cs
  57. 0 230
      src/Gtk/Avalonia.Gtk3/KeyTransform.cs
  58. 0 20
      src/Gtk/Avalonia.Gtk3/PlatformIconLoader.cs
  59. 0 19
      src/Gtk/Avalonia.Gtk3/PopupImpl.cs
  60. 0 10
      src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs
  61. 0 8
      src/Gtk/Avalonia.Gtk3/README.md
  62. 0 56
      src/Gtk/Avalonia.Gtk3/ScreenImpl.cs
  63. 0 110
      src/Gtk/Avalonia.Gtk3/SystemDialogs.cs
  64. 0 527
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  65. 0 102
      src/Gtk/Avalonia.Gtk3/WindowImpl.cs
  66. 0 78
      src/Gtk/Avalonia.Gtk3/X11.cs
  67. 0 55
      src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs
  68. 1 1
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  69. 55 19
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  70. 7 5
      src/Linux/Avalonia.LinuxFramebuffer/Mice.cs
  71. 12 16
      src/Markup/Avalonia.Markup/Data/MultiBinding.cs
  72. 0 120
      tests/Avalonia.Controls.UnitTests/ApplicationTests.cs
  73. 1 3
      tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
  74. 213 0
      tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs
  75. 38 1
      tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs
  76. 113 2
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  77. 493 30
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
  78. 0 19
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  79. 0 6
      tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
  80. 0 76
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  81. 31 9
      tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs
  82. 19 0
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  83. 31 0
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs
  84. 1 2
      tests/Avalonia.UnitTests/UnitTestApplication.cs

+ 0 - 29
Avalonia.sln

@@ -63,8 +63,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DE
 		src\Shared\SharedAssemblyInfo.cs = src\Shared\SharedAssemblyInfo.cs
 	EndProjectSection
 EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gtk", "Gtk", "{B9894058-278A-46B5-B6ED-AD613FCC03B3}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}"
 EndProject
 Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "PlatformSupport", "src\Shared\PlatformSupport\PlatformSupport.shproj", "{E4D9629C-F168-4224-3F51-A5E482FFBC42}"
@@ -123,8 +121,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Android", "s
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia", "src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{7D2D3083-71DD-4CC9-8907-39A0D86FB322}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Gtk3", "src\Gtk\Avalonia.Gtk3\Avalonia.Gtk3.csproj", "{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "samples\ControlCatalog.NetCore\ControlCatalog.NetCore.csproj", "{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
@@ -1318,30 +1314,6 @@ Global
 		{7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhone.Build.0 = Release|Any CPU
 		{7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
 		{7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|Any CPU.Build.0 = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhone.ActiveCfg = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhone.Build.0 = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhone.ActiveCfg = Debug|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhone.Build.0 = Debug|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|Any CPU.Build.0 = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhone.ActiveCfg = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhone.Build.0 = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
 		{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
 		{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
 		{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@@ -1911,7 +1883,6 @@ Global
 		{F1FDC5B0-4654-416F-AE69-E3E9BBD87801} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
-		{BB1F7BB5-6AD4-4776-94D9-C09D0A972658} = {B9894058-278A-46B5-B6ED-AD613FCC03B3}
 		{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
 		{638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}

+ 23 - 5
nukebuild/Build.cs

@@ -19,7 +19,7 @@ using static Nuke.Common.IO.PathConstruction;
 using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
 using static Nuke.Common.Tools.DotNet.DotNetTasks;
 using static Nuke.Common.Tools.Xunit.XunitTasks;
-
+using static Nuke.Common.Tools.VSWhere.VSWhereTasks;
 
 /*
  Before editing this file, install support plugin for your IDE,
@@ -30,7 +30,26 @@ using static Nuke.Common.Tools.Xunit.XunitTasks;
  */
 
 partial class Build : NukeBuild
-{   
+{
+    static Lazy<string> MsBuildExe = new Lazy<string>(() =>
+    {
+        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            return null;
+
+        var msBuildDirectory = VSWhere("-latest -nologo -property installationPath -format value -prerelease").FirstOrDefault().Text;
+
+        if (!string.IsNullOrWhiteSpace(msBuildDirectory))
+        {
+            string msBuildExe = Path.Combine(msBuildDirectory, @"MSBuild\Current\Bin\MSBuild.exe");
+            if (!System.IO.File.Exists(msBuildExe))
+                msBuildExe = Path.Combine(msBuildDirectory, @"MSBuild\15.0\Bin\MSBuild.exe");
+
+            return msBuildExe;
+        }
+
+        return null;
+    }, false);
+
     BuildParameters Parameters { get; set; }
     protected override void OnBuildInitialized()
     {
@@ -85,7 +104,6 @@ partial class Build : NukeBuild
         .DependsOn(Clean)
         .Executes(() =>
         {
-
             if (Parameters.IsRunningOnWindows)
                 MSBuild(Parameters.MSBuildSolution, c => c
                     .SetArgumentConfigurator(a => a.Add("/r"))
@@ -93,7 +111,7 @@ partial class Build : NukeBuild
                     .SetVerbosity(MSBuildVerbosity.Minimal)
                     .AddProperty("PackageVersion", Parameters.Version)
                     .AddProperty("iOSRoslynPathHackRequired", "true")
-                    .SetToolsVersion(MSBuildToolsVersion._15_0)
+                    .SetToolPath(MsBuildExe.Value)
                     .AddTargets("Build")
                 );
 
@@ -224,7 +242,7 @@ partial class Build : NukeBuild
                     .SetVerbosity(MSBuildVerbosity.Minimal)
                     .AddProperty("PackageVersion", Parameters.Version)
                     .AddProperty("iOSRoslynPathHackRequired", "true")
-                    .SetToolsVersion(MSBuildToolsVersion._15_0)
+                    .SetToolPath(MsBuildExe.Value)
                     .AddTargets("Pack"));
             else
                 DotNetPack(Parameters.MSBuildSolution, c =>

+ 2 - 1
nukebuild/_build.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
@@ -13,6 +13,7 @@
     <PackageReference Include="Nuke.Common" Version="0.12.3" />
     <PackageReference Include="xunit.runner.console" Version="2.3.1" />
     <PackageReference Include="JetBrains.dotMemoryUnit" Version="3.0.20171219.105559" />
+    <PackageReference Include="vswhere" Version="2.6.7" Condition=" '$(OS)' == 'Windows_NT' " />
   </ItemGroup>
 
   <ItemGroup>

+ 9 - 13
samples/ControlCatalog.NetCore/Program.cs

@@ -3,6 +3,7 @@ using System.Diagnostics;
 using System.Linq;
 using System.Threading;
 using Avalonia;
+using Avalonia.Controls;
 using Avalonia.Skia;
 using Avalonia.ReactiveUI;
 
@@ -11,7 +12,7 @@ namespace ControlCatalog.NetCore
     static class Program
     {
 
-        static void Main(string[] args)
+        static int Main(string[] args)
         {
             Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA);
             if (args.Contains("--wait-for-attach"))
@@ -25,21 +26,16 @@ namespace ControlCatalog.NetCore
                 }
             }
 
+            var builder = BuildAvaloniaApp();
             if (args.Contains("--fbdev"))
-                AppBuilder.Configure<App>().InitializeWithLinuxFramebuffer(tl =>
-                {
-                    tl.Content = new MainView();
-                    System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
-                });
+            {
+                System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer());
+                return builder.StartLinuxFramebuffer(args);
+            }
             else
-                BuildAvaloniaApp().Start(AppMain, args);
+                return builder.StartWithClassicDesktopLifetime(args);
         }
-
-        static void AppMain(Application app, string[] args)
-        {
-            app.Run(new MainWindow());
-        }
-
+        
         /// <summary>
         /// This method is needed for IDE previewer infrastructure
         /// </summary>

+ 11 - 0
samples/ControlCatalog/App.xaml.cs

@@ -1,4 +1,5 @@
 using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Markup.Xaml;
 
 namespace ControlCatalog
@@ -9,5 +10,15 @@ namespace ControlCatalog
         {
             AvaloniaXamlLoader.Load(this);
         }
+
+        public override void OnFrameworkInitializationCompleted()
+        {
+            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
+                desktopLifetime.MainWindow = new MainWindow();
+            else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime)
+                singleViewLifetime.MainView = new MainView();
+            
+            base.OnFrameworkInitializationCompleted();
+        }
     }
 }

+ 46 - 0
src/Avalonia.Base/Data/Converters/StringFormatMultiValueConverter.cs

@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+
+namespace Avalonia.Data.Converters
+{
+    /// <summary>
+    /// A multi-value converter which calls <see cref="string.Format(string, object)"/>
+    /// </summary>
+    public class StringFormatMultiValueConverter : IMultiValueConverter
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="StringFormatMultiValueConverter"/> class.
+        /// </summary>
+        /// <param name="format">The format string.</param>
+        /// <param name="inner">
+        /// An optional inner converter to be called before the format takes place.
+        /// </param>
+        public StringFormatMultiValueConverter(string format, IMultiValueConverter inner)
+        {
+            Contract.Requires<ArgumentNullException>(format != null);
+
+            Format = format;
+            Inner = inner;
+        }
+
+        /// <summary>
+        /// Gets an inner value converter which will be called before the string format takes place.
+        /// </summary>
+        public IMultiValueConverter Inner { get; }
+
+        /// <summary>
+        /// Gets the format string.
+        /// </summary>
+        public string Format { get; }
+
+        /// <inheritdoc/>
+        public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
+        {
+            return Inner == null
+                       ? string.Format(culture, Format, values.ToArray())
+                       : string.Format(culture, Format, Inner.Convert(values, targetType, parameter, culture));
+        }
+    }
+}

+ 19 - 54
src/Avalonia.Controls/AppBuilderBase.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Reflection;
 using System.Linq;
+using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Platform;
 
 namespace Avalonia.Controls
@@ -57,10 +58,6 @@ namespace Avalonia.Controls
         /// </summary>
         public Action<TAppBuilder> AfterSetupCallback { get; private set; } = builder => { };
 
-        /// <summary>
-        /// Gets or sets a method to call before Start is called on the <see cref="Application"/>.
-        /// </summary>
-        public Action<TAppBuilder> BeforeStartCallback { get; private set; } = builder => { };
 
         protected AppBuilderBase(IRuntimePlatform platform, Action<TAppBuilder> platformServices)
         {
@@ -95,17 +92,6 @@ namespace Avalonia.Controls
 
         protected TAppBuilder Self => (TAppBuilder)this;
 
-        /// <summary>
-        /// Registers a callback to call before Start is called on the <see cref="Application"/>.
-        /// </summary>
-        /// <param name="callback">The callback.</param>
-        /// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
-        public TAppBuilder BeforeStarting(Action<TAppBuilder> callback)
-        {
-            BeforeStartCallback = (Action<TAppBuilder>)Delegate.Combine(BeforeStartCallback, callback);
-            return Self;
-        }
-
         public TAppBuilder AfterSetup(Action<TAppBuilder> callback)
         {
             AfterSetupCallback = (Action<TAppBuilder>)Delegate.Combine(AfterSetupCallback, callback);
@@ -117,48 +103,37 @@ namespace Avalonia.Controls
         /// </summary>
         /// <typeparam name="TMainWindow">The window type.</typeparam>
         /// <param name="dataContextProvider">A delegate that will be called to create a data context for the window (optional).</param>
+        [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details")]
         public void Start<TMainWindow>(Func<object> dataContextProvider = null)
             where TMainWindow : Window, new()
         {
-            Setup();
-            BeforeStartCallback(Self);
-
-            var window = new TMainWindow();
-            if (dataContextProvider != null)
-                window.DataContext = dataContextProvider();
-            Instance.Run(window);
-        }
-
-        /// <summary>
-        /// Starts the application with the provided instance of <typeparamref name="TMainWindow"/>.
-        /// </summary>
-        /// <typeparam name="TMainWindow">The window type.</typeparam>
-        /// <param name="mainWindow">Instance of type TMainWindow to use when starting the app</param>
-        /// <param name="dataContextProvider">A delegate that will be called to create a data context for the window (optional).</param>
-        public void Start<TMainWindow>(TMainWindow mainWindow, Func<object> dataContextProvider = null)
-            where TMainWindow : Window
-        {
-            Setup();
-            BeforeStartCallback(Self);
-
-            if (dataContextProvider != null)
-                mainWindow.DataContext = dataContextProvider();
-            Instance.Run(mainWindow);
+            AfterSetup(builder =>
+            {
+                var window = new TMainWindow();
+                if (dataContextProvider != null)
+                    window.DataContext = dataContextProvider();
+                ((IClassicDesktopStyleApplicationLifetime)builder.Instance.ApplicationLifetime)
+                    .MainWindow = window;
+            });
+            
+            // Copy-pasted because we can't call extension methods due to generic constraints
+            var lifetime = new ClassicDesktopStyleApplicationLifetime(Instance) {ShutdownMode = ShutdownMode.OnMainWindowClose};
+            Instance.ApplicationLifetime = lifetime;
+            SetupWithoutStarting();
+            lifetime.Start(Array.Empty<string>());
         }
 
         public delegate void AppMainDelegate(Application app, string[] args);
 
+        [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)]
         public void Start()
         {
-            Setup();
-            BeforeStartCallback(Self);
-            Instance.Run();
+            throw new NotSupportedException();
         }
 
         public void Start(AppMainDelegate main, string[] args)
         {
             Setup();
-            BeforeStartCallback(Self);
             main(Instance, args);
         }
 
@@ -224,17 +199,6 @@ namespace Avalonia.Controls
 
         public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules());
 
-        /// <summary>
-        /// Sets the shutdown mode of the application.
-        /// </summary>
-        /// <param name="shutdownMode">The shutdown mode.</param>
-        /// <returns></returns>
-        public TAppBuilder SetShutdownMode(ShutdownMode shutdownMode)
-        {
-            Instance.ShutdownMode = shutdownMode;
-            return Self;
-        }
-
         protected virtual bool CheckSetup => true;
 
         private void SetupAvaloniaModules()
@@ -313,6 +277,7 @@ namespace Avalonia.Controls
             Instance.RegisterServices();
             Instance.Initialize();
             AfterSetupCallback(Self);
+            Instance.OnFrameworkInitializationCompleted();
         }
     }
 }

+ 14 - 202
src/Avalonia.Controls/Application.cs

@@ -6,6 +6,7 @@ using System.Reactive.Concurrency;
 using System.Threading;
 using Avalonia.Animation;
 using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Controls.Templates;
 using Avalonia.Input;
 using Avalonia.Input.Platform;
@@ -31,7 +32,7 @@ namespace Avalonia
     /// method.
     /// - Tracks the lifetime of the application.
     /// </remarks>
-    public class Application : IApplicationLifecycle, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
+    public class Application : IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
     {
         /// <summary>
         /// The application-global data templates.
@@ -43,22 +44,6 @@ namespace Avalonia
         private readonly Styler _styler = new Styler();
         private Styles _styles;
         private IResourceDictionary _resources;
-        private CancellationTokenSource _mainLoopCancellationTokenSource;
-        private int _exitCode;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="Application"/> class.
-        /// </summary>
-        public Application()
-        {
-            Windows = new WindowCollection(this);
-        }
-
-        /// <inheritdoc/>
-        public event EventHandler<StartupEventArgs> Startup;
-
-        /// <inheritdoc/>
-        public event EventHandler<ExitEventArgs> Exit;
 
         /// <inheritdoc/>
         public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
@@ -166,198 +151,21 @@ namespace Avalonia
 
         /// <inheritdoc/>
         IResourceNode IResourceNode.ResourceParent => null;
-
-        /// <summary>
-        /// Gets or sets the <see cref="ShutdownMode"/>. This property indicates whether the application is shutdown explicitly or implicitly. 
-        /// If <see cref="ShutdownMode"/> is set to OnExplicitShutdown the application is only closes if Shutdown is called.
-        /// The default is OnLastWindowClose
-        /// </summary>
-        /// <value>
-        /// The shutdown mode.
-        /// </value>
-        public ShutdownMode ShutdownMode { get; set; }
-
-        /// <summary>
-        /// Gets or sets the main window of the application.
-        /// </summary>
-        /// <value>
-        /// The main window.
-        /// </value>
-        public Window MainWindow { get; set; }
-
-        /// <summary>
-        /// Gets the open windows of the application.
-        /// </summary>
-        /// <value>
-        /// The windows.
-        /// </value>
-        public WindowCollection Windows { get; }
-
+        
         /// <summary>
-        /// Gets or sets a value indicating whether this instance is shutting down.
+        /// Application lifetime, use it for things like setting the main window and exiting the app from code
+        /// Currently supported lifetimes are:
+        /// - <see cref="IClassicDesktopStyleApplicationLifetime"/>
+        /// - <see cref="ISingleViewApplicationLifetime"/>
+        /// - <see cref="IControlledApplicationLifetime"/> 
         /// </summary>
-        /// <value>
-        ///   <c>true</c> if this instance is shutting down; otherwise, <c>false</c>.
-        /// </value>
-        internal bool IsShuttingDown { get; private set; }
+        public IApplicationLifetime ApplicationLifetime { get; set; }
 
         /// <summary>
         /// Initializes the application by loading XAML etc.
         /// </summary>
         public virtual void Initialize() { }
 
-        /// <summary>
-        /// Runs the application's main loop.
-        /// </summary>
-        /// <remarks>
-        /// This will return when the <see cref="Avalonia.Controls.ShutdownMode"/> condition is met
-        /// or <see cref="Shutdown(int)"/> was called. 
-        /// </remarks>
-        /// <returns>The application's exit code that is returned to the operating system on termination.</returns>
-        public int Run()
-        {
-            return Run(new CancellationTokenSource());
-        }
-
-        /// <summary>
-        /// Runs the application's main loop.
-        /// </summary>
-        /// <remarks>
-        /// This will return when the <see cref="Avalonia.Controls.ShutdownMode"/> condition is met
-        /// or <see cref="Shutdown(int)"/> was called.
-        /// This also returns when <see cref="ICloseable"/> is closed.
-        /// </remarks>
-        /// <param name="closable">The closable to track.</param>
-        /// <returns>The application's exit code that is returned to the operating system on termination.</returns>
-        public int Run(ICloseable closable)
-        {
-            closable.Closed += (s, e) => _mainLoopCancellationTokenSource?.Cancel();
-
-            return Run(new CancellationTokenSource());
-        }
-
-        /// <summary>
-        /// Runs the application's main loop.
-        /// </summary>
-        /// <remarks>
-        /// This will return when the <see cref="Avalonia.Controls.ShutdownMode"/> condition is met
-        /// or <see cref="Shutdown(int)"/> was called.
-        /// </remarks>
-        /// <param name="mainWindow">The window that is used as <see cref="MainWindow"/>
-        /// when the <see cref="MainWindow"/> isn't already set.</param>
-        /// <returns>The application's exit code that is returned to the operating system on termination.</returns>
-        public int Run(Window mainWindow)
-        {
-            if (mainWindow == null)
-            {
-                throw new ArgumentNullException(nameof(mainWindow));
-            }
-
-            if (MainWindow == null)
-            {
-                if (!mainWindow.IsVisible)
-                {
-                    mainWindow.Show();
-                }
-
-                MainWindow = mainWindow;
-            }
-
-            return Run(new CancellationTokenSource());
-        }
-        /// <summary>
-        /// Runs the application's main loop.
-        /// </summary>
-        /// <remarks>
-        /// This will return when the <see cref="Avalonia.Controls.ShutdownMode"/> condition is met
-        /// or <see cref="Shutdown(int)"/> was called.
-        /// This also returns when the <see cref="CancellationToken"/> is canceled.
-        /// </remarks>
-        /// <returns>The application's exit code that is returned to the operating system on termination.</returns>
-        /// <param name="token">The token to track.</param>
-        public int Run(CancellationToken token)
-        {
-            return Run(CancellationTokenSource.CreateLinkedTokenSource(token));
-        }
-
-        private int Run(CancellationTokenSource tokenSource)
-        {
-            if (IsShuttingDown)
-            {
-                throw new InvalidOperationException("Application is shutting down.");
-            }
-
-            if (_mainLoopCancellationTokenSource != null)
-            {
-                throw new InvalidOperationException("Application is already running.");
-            }
-
-            _mainLoopCancellationTokenSource = tokenSource;
-
-            Dispatcher.UIThread.Post(() => OnStartup(new StartupEventArgs()), DispatcherPriority.Send);
-
-            Dispatcher.UIThread.MainLoop(_mainLoopCancellationTokenSource.Token);
-
-            if (!IsShuttingDown)
-            {
-                Shutdown(_exitCode);
-            }
-
-            return _exitCode;
-        }
-
-        /// <summary>
-        /// Raises the <see cref="Startup"/> event.
-        /// </summary>
-        /// <param name="e">A <see cref="StartupEventArgs"/> that contains the event data.</param>
-        protected virtual void OnStartup(StartupEventArgs e)
-        {
-            Startup?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Raises the <see cref="Exit"/> event.
-        /// </summary>
-        /// <param name="e">A <see cref="ExitEventArgs"/> that contains the event data.</param>
-        protected virtual void OnExit(ExitEventArgs e)
-        {
-            Exit?.Invoke(this, e);
-        }
-
-        /// <inheritdoc/>
-        public void Shutdown(int exitCode = 0)
-        {
-            if (IsShuttingDown)
-            {
-                throw new InvalidOperationException("Application is already shutting down.");
-            }
-
-            _exitCode = exitCode;
-
-            IsShuttingDown = true;         
-
-            Windows.Clear();
-
-            try
-            {
-                var e = new ExitEventArgs { ApplicationExitCode = _exitCode };
-
-                OnExit(e);
-
-                _exitCode = e.ApplicationExitCode;                
-            }
-            finally
-            {
-                _mainLoopCancellationTokenSource?.Cancel();
-
-                _mainLoopCancellationTokenSource = null;
-
-                IsShuttingDown = false;
-
-                Environment.ExitCode = _exitCode;
-            }
-        }
-
         /// <inheritdoc/>
         bool IResourceProvider.TryGetResource(object key, out object value)
         {
@@ -383,7 +191,6 @@ namespace Avalonia
                 .Bind<IInputManager>().ToConstant(InputManager)
                 .Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()
                 .Bind<IStyler>().ToConstant(_styler)
-                .Bind<IApplicationLifecycle>().ToConstant(this)
                 .Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance)
                 .Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance)
                 .Bind<IPlatformDragSource>().ToTransient<InProcessDragSource>();
@@ -394,6 +201,11 @@ namespace Avalonia
                 .GetService<IRenderLoop>()?.Add(clock);
         }
 
+        public virtual void OnFrameworkInitializationCompleted()
+        {
+            
+        }
+
         private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
         {
             ResourcesChanged?.Invoke(this, e);

+ 133 - 0
src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs

@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Interactivity;
+
+namespace Avalonia.Controls.ApplicationLifetimes
+{
+    public class ClassicDesktopStyleApplicationLifetime : IClassicDesktopStyleApplicationLifetime, IDisposable
+    {
+        private readonly Application _app;
+        private int _exitCode;
+        private CancellationTokenSource _cts;
+        private bool _isShuttingDown;
+        private HashSet<Window> _windows = new HashSet<Window>();
+
+        private static ClassicDesktopStyleApplicationLifetime _activeLifetime;
+        static ClassicDesktopStyleApplicationLifetime()
+        {
+            Window.WindowOpenedEvent.AddClassHandler(typeof(Window), OnWindowOpened);
+            Window.WindowClosedEvent.AddClassHandler(typeof(Window), WindowClosedEvent);
+        }
+
+        private static void WindowClosedEvent(object sender, RoutedEventArgs e)
+        {
+            _activeLifetime?._windows.Remove((Window)sender);
+            _activeLifetime?.HandleWindowClosed((Window)sender);
+        }
+
+        private static void OnWindowOpened(object sender, RoutedEventArgs e)
+        {
+            _activeLifetime?._windows.Add((Window)sender);
+        }
+
+        public ClassicDesktopStyleApplicationLifetime(Application app)
+        {
+            if (_activeLifetime != null)
+                throw new InvalidOperationException(
+                    "Can not have multiple active ClassicDesktopStyleApplicationLifetime instances and the previously created one was not disposed");
+            _app = app;
+            _activeLifetime = this;
+        }
+        
+        /// <inheritdoc/>
+        public event EventHandler<ControlledApplicationLifetimeStartupEventArgs> Startup;
+        /// <inheritdoc/>
+        public event EventHandler<ControlledApplicationLifetimeExitEventArgs> Exit;
+
+        /// <inheritdoc/>
+        public ShutdownMode ShutdownMode { get; set; }
+        
+        /// <inheritdoc/>
+        public Window MainWindow { get; set; }
+
+        public IReadOnlyList<Window> Windows => _windows.ToList();
+
+        private void HandleWindowClosed(Window window)
+        {
+            if (window == null)
+                return;
+            
+            if (_isShuttingDown)
+                return;
+
+            if (ShutdownMode == ShutdownMode.OnLastWindowClose && _windows.Count == 0)
+                Shutdown();
+            else if (ShutdownMode == ShutdownMode.OnMainWindowClose && window == MainWindow)
+                Shutdown();
+        }
+        
+        
+
+
+        public void Shutdown(int exitCode = 0)
+        {
+            if (_isShuttingDown)
+                throw new InvalidOperationException("Application is already shutting down.");
+            
+            _exitCode = exitCode;
+            _isShuttingDown = true;
+
+            try
+            {
+                foreach (var w in Windows)
+                    w.Close();
+                var e = new ControlledApplicationLifetimeExitEventArgs(exitCode);
+                Exit?.Invoke(this, e);
+                _exitCode = e.ApplicationExitCode;                
+            }
+            finally
+            {
+                _cts?.Cancel();
+                _cts = null;
+                _isShuttingDown = false;
+            }
+        }
+        
+        
+        public int Start(string[] args)
+        {
+            Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args));
+            _cts = new CancellationTokenSource();
+            MainWindow?.Show();
+            _app.Run(_cts.Token);
+            Environment.ExitCode = _exitCode;
+            return _exitCode;
+        }
+
+        public void Dispose()
+        {
+            if (_activeLifetime == this)
+                _activeLifetime = null;
+        }
+    }
+}
+
+namespace Avalonia
+{
+    public static class ClassicDesktopStyleApplicationLifetimeExtensions
+    {
+        public static int StartWithClassicDesktopLifetime<T>(
+            this T builder, string[] args, ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)
+            where T : AppBuilderBase<T>, new()
+        {
+            var lifetime = new ClassicDesktopStyleApplicationLifetime(builder.Instance) {ShutdownMode = shutdownMode};
+            builder.Instance.ApplicationLifetime = lifetime;
+            builder.SetupWithoutStarting();
+            return lifetime.Start(args);
+        }
+    }
+}

+ 8 - 3
src/Avalonia.Controls/ExitEventArgs.cs → src/Avalonia.Controls/ApplicationLifetimes/ControlledApplicationLifetimeExitEventArgs.cs

@@ -3,13 +3,18 @@
 
 using System;
 
-namespace Avalonia.Controls
+namespace Avalonia.Controls.ApplicationLifetimes
 {
     /// <summary>
-    /// Contains the arguments for the <see cref="IApplicationLifecycle.Exit"/> event.
+    /// Contains the arguments for the <see cref="IClassicDesktopStyleApplicationLifetime.Exit"/> event.
     /// </summary>
-    public class ExitEventArgs : EventArgs
+    public class ControlledApplicationLifetimeExitEventArgs : EventArgs
     {
+        public ControlledApplicationLifetimeExitEventArgs(int applicationExitCode)
+        {
+            ApplicationExitCode = applicationExitCode;
+        }
+
         /// <summary>
         /// Gets or sets the exit code that an application returns to the operating system when the application exits.
         /// </summary>

+ 7 - 0
src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs

@@ -0,0 +1,7 @@
+namespace Avalonia.Controls.ApplicationLifetimes
+{
+    public interface IApplicationLifetime
+    {
+        
+    }
+}

+ 31 - 0
src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+
+namespace Avalonia.Controls.ApplicationLifetimes
+{
+    /// <summary>
+    /// Controls application lifetime in classic desktop style
+    /// </summary>
+    public interface IClassicDesktopStyleApplicationLifetime : IControlledApplicationLifetime
+    {
+        /// <summary>
+        /// Gets or sets the <see cref="ShutdownMode"/>. This property indicates whether the application is shutdown explicitly or implicitly. 
+        /// If <see cref="ShutdownMode"/> is set to OnExplicitShutdown the application is only closes if Shutdown is called.
+        /// The default is OnLastWindowClose
+        /// </summary>
+        /// <value>
+        /// The shutdown mode.
+        /// </value>
+        ShutdownMode ShutdownMode { get; set; }
+
+        /// <summary>
+        /// Gets or sets the main window of the application.
+        /// </summary>
+        /// <value>
+        /// The main window.
+        /// </value>
+        Window MainWindow { get; set; }
+        
+        IReadOnlyList<Window> Windows { get; }
+    }
+}

+ 5 - 8
src/Avalonia.Controls/IApplicationLifecycle.cs → src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs

@@ -1,22 +1,19 @@
 using System;
 
-namespace Avalonia.Controls
+namespace Avalonia.Controls.ApplicationLifetimes
 {
-    /// <summary>
-    /// Sends events about the application lifecycle.
-    /// </summary>
-    public interface IApplicationLifecycle
+    public interface IControlledApplicationLifetime : IApplicationLifetime
     {
         /// <summary>
         /// Sent when the application is starting up.
         /// </summary>
-        event EventHandler<StartupEventArgs> Startup;
+        event EventHandler<ControlledApplicationLifetimeStartupEventArgs> Startup;
 
         /// <summary>
         /// Sent when the application is exiting.
         /// </summary>
-        event EventHandler<ExitEventArgs> Exit;
-
+        event EventHandler<ControlledApplicationLifetimeExitEventArgs> Exit;
+        
         /// <summary>
         /// Shuts down the application and sets the exit code that is returned to the operating system when the application exits.
         /// </summary>

+ 7 - 0
src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs

@@ -0,0 +1,7 @@
+namespace Avalonia.Controls.ApplicationLifetimes
+{
+    public interface ISingleViewApplicationLifetime : IApplicationLifetime
+    {
+        Control MainView { get; set; }
+    }
+}

+ 22 - 0
src/Avalonia.Controls/ApplicationLifetimes/StartupEventArgs.cs

@@ -0,0 +1,22 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Avalonia.Controls.ApplicationLifetimes
+{
+    /// <summary>
+    /// Contains the arguments for the <see cref="IClassicDesktopStyleApplicationLifetime.Startup"/> event.
+    /// </summary>
+    public class ControlledApplicationLifetimeStartupEventArgs : EventArgs
+    {
+        public ControlledApplicationLifetimeStartupEventArgs(IEnumerable<string> args)
+        {
+            Args = args?.ToArray() ?? Array.Empty<string>();
+        }
+
+        public string[] Args { get;  }
+    }
+}

+ 27 - 6
src/Avalonia.Controls/ColumnDefinition.cs

@@ -62,8 +62,15 @@ namespace Avalonia.Controls
         /// </summary>
         public double MaxWidth
         {
-            get { return GetValue(MaxWidthProperty); }
-            set { SetValue(MaxWidthProperty, value); }
+            get
+            {
+                return GetValue(MaxWidthProperty);
+            }
+            set
+            {
+                Parent?.InvalidateMeasure();
+                SetValue(MaxWidthProperty, value);
+            }
         }
 
         /// <summary>
@@ -71,8 +78,15 @@ namespace Avalonia.Controls
         /// </summary>
         public double MinWidth
         {
-            get { return GetValue(MinWidthProperty); }
-            set { SetValue(MinWidthProperty, value); }
+            get
+            {
+                return GetValue(MinWidthProperty);
+            }
+            set
+            {
+                Parent?.InvalidateMeasure();
+                SetValue(MinWidthProperty, value);
+            }
         }
 
         /// <summary>
@@ -80,8 +94,15 @@ namespace Avalonia.Controls
         /// </summary>
         public GridLength Width
         {
-            get { return GetValue(WidthProperty); }
-            set { SetValue(WidthProperty, value); }
+            get
+            {
+                return GetValue(WidthProperty);
+            }
+            set
+            {
+                Parent?.InvalidateMeasure();
+                SetValue(WidthProperty, value);
+            }
         }
 
         internal override GridLength UserSizeValueCache => this.Width;

+ 67 - 0
src/Avalonia.Controls/DesktopApplicationExtensions.cs

@@ -0,0 +1,67 @@
+using System;
+using System.Threading;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Threading;
+
+namespace Avalonia.Controls
+{
+    public static class DesktopApplicationExtensions
+    {
+        [Obsolete("Running application without a cancellation token and a lifetime is no longer supported, see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)]
+        public static void Run(this Application app) => throw new NotSupportedException();
+
+        /// <summary>
+        /// On desktop-style platforms runs the application's main loop until closable is closed
+        /// </summary>
+        /// <remarks>
+        /// Consider using StartWithDesktopStyleLifetime instead, see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details
+        /// </remarks>
+        public static void Run(this Application app, ICloseable closable)
+        {
+            var cts = new CancellationTokenSource();
+            closable.Closed += (s, e) => cts.Cancel();
+
+            app.Run(cts.Token);
+        }
+
+        /// <summary>
+        /// On desktop-style platforms runs the application's main loop until main window is closed
+        /// </summary>
+        /// <remarks>
+        /// Consider using StartWithDesktopStyleLifetime instead, see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details
+        /// </remarks>
+        public static void Run(this Application app, Window mainWindow)
+        {
+            if (mainWindow == null)
+            {
+                throw new ArgumentNullException(nameof(mainWindow));
+            }
+            var cts = new CancellationTokenSource();
+            mainWindow.Closed += (_, __) => cts.Cancel();
+            if (!mainWindow.IsVisible)
+            {
+                mainWindow.Show();
+            }
+            app.Run(cts.Token);
+        }
+        
+        /// <summary>
+        /// On desktop-style platforms runs the application's main loop with custom CancellationToken
+        /// without setting a lifetime.
+        /// </summary>
+        /// <param name="token">The token to track.</param>
+        public static void Run(this Application app, CancellationToken token)
+        {
+            Dispatcher.UIThread.MainLoop(token);
+        }
+
+        public static void RunWithMainWindow<TWindow>(this Application app)
+            where TWindow : Avalonia.Controls.Window, new()
+        {
+            var window = new TWindow();
+            window.Show();
+            app.Run(window);
+        }
+    }
+}

+ 104 - 69
src/Avalonia.Controls/DockPanel.cs

@@ -1,7 +1,12 @@
+// This source file is adapted from the Windows Presentation Foundation project. 
+// (https://github.com/dotnet/wpf/) 
+// 
+// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
+
+using System;
+
 namespace Avalonia.Controls
 {
-    using System;
-
     /// <summary>
     /// Defines the available docking modes for a control in a <see cref="DockPanel"/>.
     /// </summary>
@@ -70,107 +75,137 @@ namespace Avalonia.Controls
             set { SetValue(LastChildFillProperty, value); }
         }
 
-        /// <inheritdoc/>
+        /// <summary>
+        /// Updates DesiredSize of the DockPanel.  Called by parent Control.  This is the first pass of layout.
+        /// </summary>
+        /// <remarks>
+        /// Children are measured based on their sizing properties and <see cref="Dock" />.  
+        /// Each child is allowed to consume all of the space on the side on which it is docked; Left/Right docked
+        /// children are granted all vertical space for their entire width, and Top/Bottom docked children are
+        /// granted all horizontal space for their entire height.
+        /// </remarks>
+        /// <param name="constraint">Constraint size is an "upper limit" that the return value should not exceed.</param>
+        /// <returns>The Panel's desired size.</returns>
         protected override Size MeasureOverride(Size constraint)
         {
-            double usedWidth = 0.0;
-            double usedHeight = 0.0;
-            double maximumWidth = 0.0;
-            double maximumHeight = 0.0;
+            var children = Children;
+
+            double parentWidth = 0;   // Our current required width due to children thus far.
+            double parentHeight = 0;   // Our current required height due to children thus far.
+            double accumulatedWidth = 0;   // Total width consumed by children.
+            double accumulatedHeight = 0;   // Total height consumed by children.
 
-            // Measure each of the Children
-            foreach (Control element in Children)
+            for (int i = 0, count = children.Count; i < count; ++i)
             {
-                // Get the child's desired size
-                Size remainingSize = new Size(
-                    Math.Max(0.0, constraint.Width - usedWidth),
-                    Math.Max(0.0, constraint.Height - usedHeight));
-                element.Measure(remainingSize);
-                Size desiredSize = element.DesiredSize;
-
-                // Decrease the remaining space for the rest of the children
-                switch (GetDock(element))
+                var child = children[i];
+                Size childConstraint;             // Contains the suggested input constraint for this child.
+                Size childDesiredSize;            // Contains the return size from child measure.
+
+                if (child == null)
+                { continue; }
+
+                // Child constraint is the remaining size; this is total size minus size consumed by previous children.
+                childConstraint = new Size(Math.Max(0.0, constraint.Width - accumulatedWidth),
+                                           Math.Max(0.0, constraint.Height - accumulatedHeight));
+
+                // Measure child.
+                child.Measure(childConstraint);
+                childDesiredSize = child.DesiredSize;
+
+                // Now, we adjust:
+                // 1. Size consumed by children (accumulatedSize).  This will be used when computing subsequent
+                //    children to determine how much space is remaining for them.
+                // 2. Parent size implied by this child (parentSize) when added to the current children (accumulatedSize).
+                //    This is different from the size above in one respect: A Dock.Left child implies a height, but does
+                //    not actually consume any height for subsequent children.
+                // If we accumulate size in a given dimension, the next child (or the end conditions after the child loop)
+                // will deal with computing our minimum size (parentSize) due to that accumulation.
+                // Therefore, we only need to compute our minimum size (parentSize) in dimensions that this child does
+                //   not accumulate: Width for Top/Bottom, Height for Left/Right.
+                switch (DockPanel.GetDock((Control)child))
                 {
                     case Dock.Left:
                     case Dock.Right:
-                        maximumHeight = Math.Max(maximumHeight, usedHeight + desiredSize.Height);
-                        usedWidth += desiredSize.Width;
+                        parentHeight = Math.Max(parentHeight, accumulatedHeight + childDesiredSize.Height);
+                        accumulatedWidth += childDesiredSize.Width;
                         break;
+
                     case Dock.Top:
                     case Dock.Bottom:
-                        maximumWidth = Math.Max(maximumWidth, usedWidth + desiredSize.Width);
-                        usedHeight += desiredSize.Height;
+                        parentWidth = Math.Max(parentWidth, accumulatedWidth + childDesiredSize.Width);
+                        accumulatedHeight += childDesiredSize.Height;
                         break;
                 }
             }
 
-            maximumWidth = Math.Max(maximumWidth, usedWidth);
-            maximumHeight = Math.Max(maximumHeight, usedHeight);
-            return new Size(maximumWidth, maximumHeight);
+            // Make sure the final accumulated size is reflected in parentSize.
+            parentWidth = Math.Max(parentWidth, accumulatedWidth);
+            parentHeight = Math.Max(parentHeight, accumulatedHeight);
+
+            return (new Size(parentWidth, parentHeight));
         }
 
-        /// <inheritdoc/>
+        /// <summary>
+        /// DockPanel computes a position and final size for each of its children based upon their
+        /// <see cref="Dock" /> enum and sizing properties.
+        /// </summary>
+        /// <param name="arrangeSize">Size that DockPanel will assume to position children.</param>
         protected override Size ArrangeOverride(Size arrangeSize)
         {
-            double left = 0.0;
-            double top = 0.0;
-            double right = 0.0;
-            double bottom = 0.0;
-
-            // Arrange each of the Children
             var children = Children;
-            int dockedCount = children.Count - (LastChildFill ? 1 : 0);
-            int index = 0;
+            int totalChildrenCount = children.Count;
+            int nonFillChildrenCount = totalChildrenCount - (LastChildFill ? 1 : 0);
+
+            double accumulatedLeft = 0;
+            double accumulatedTop = 0;
+            double accumulatedRight = 0;
+            double accumulatedBottom = 0;
 
-            foreach (Control element in children)
+            for (int i = 0; i < totalChildrenCount; ++i)
             {
-                // Determine the remaining space left to arrange the element
-                Rect remainingRect = new Rect(
-                    left,
-                    top,
-                    Math.Max(0.0, arrangeSize.Width - left - right),
-                    Math.Max(0.0, arrangeSize.Height - top - bottom));
-
-                // Trim the remaining Rect to the docked size of the element
-                // (unless the element should fill the remaining space because
-                // of LastChildFill)
-                if (index < dockedCount)
+                var child = children[i];
+                if (child == null)
+                { continue; }
+
+                Size childDesiredSize = child.DesiredSize;
+                Rect rcChild = new Rect(
+                    accumulatedLeft,
+                    accumulatedTop,
+                    Math.Max(0.0, arrangeSize.Width - (accumulatedLeft + accumulatedRight)),
+                    Math.Max(0.0, arrangeSize.Height - (accumulatedTop + accumulatedBottom)));
+
+                if (i < nonFillChildrenCount)
                 {
-                    Size desiredSize = element.DesiredSize;
-                    switch (GetDock(element))
+                    switch (DockPanel.GetDock((Control)child))
                     {
                         case Dock.Left:
-                            left += desiredSize.Width;
-                            remainingRect = remainingRect.WithWidth(desiredSize.Width);
-                            break;
-                        case Dock.Top:
-                            top += desiredSize.Height;
-                            remainingRect = remainingRect.WithHeight(desiredSize.Height);
+                            accumulatedLeft += childDesiredSize.Width;
+                            rcChild = rcChild.WithWidth(childDesiredSize.Width);
                             break;
+
                         case Dock.Right:
-                            right += desiredSize.Width;
-                            remainingRect = new Rect(
-                                Math.Max(0.0, arrangeSize.Width - right),
-                                remainingRect.Y,
-                                desiredSize.Width,
-                                remainingRect.Height);
+                            accumulatedRight += childDesiredSize.Width;
+                            rcChild = rcChild.WithX(Math.Max(0.0, arrangeSize.Width - accumulatedRight));
+                            rcChild = rcChild.WithWidth(childDesiredSize.Width);
                             break;
+
+                        case Dock.Top:
+                            accumulatedTop += childDesiredSize.Height;
+                            rcChild = rcChild.WithHeight(childDesiredSize.Height);
+                            break;
+
                         case Dock.Bottom:
-                            bottom += desiredSize.Height;
-                            remainingRect = new Rect(
-                                remainingRect.X,
-                                Math.Max(0.0, arrangeSize.Height - bottom),
-                                remainingRect.Width,
-                                desiredSize.Height);
+                            accumulatedBottom += childDesiredSize.Height;
+                            rcChild = rcChild.WithY(Math.Max(0.0, arrangeSize.Height - accumulatedBottom));
+                            rcChild = rcChild.WithHeight(childDesiredSize.Height);
                             break;
                     }
                 }
 
-                element.Arrange(remainingRect);
-                index++;
+                child.Arrange(rcChild);
             }
 
-            return arrangeSize;
+            return (arrangeSize);
         }
     }
 }

+ 12 - 2
src/Avalonia.Controls/ItemsControl.cs

@@ -36,6 +36,12 @@ namespace Avalonia.Controls
         public static readonly DirectProperty<ItemsControl, IEnumerable> ItemsProperty =
             AvaloniaProperty.RegisterDirect<ItemsControl, IEnumerable>(nameof(Items), o => o.Items, (o, v) => o.Items = v);
 
+        /// <summary>
+        /// Defines the <see cref="ItemCount"/> property.
+        /// </summary>
+        public static readonly DirectProperty<ItemsControl, int> ItemCountProperty =
+            AvaloniaProperty.RegisterDirect<ItemsControl, int>(nameof(ItemCount), o => o.ItemCount);
+
         /// <summary>
         /// Defines the <see cref="ItemsPanel"/> property.
         /// </summary>
@@ -55,6 +61,7 @@ namespace Avalonia.Controls
             AvaloniaProperty.Register<ItemsControl, IMemberSelector>(nameof(MemberSelector));
 
         private IEnumerable _items = new AvaloniaList<object>();
+        private int _itemCount;
         private IItemContainerGenerator _itemContainerGenerator;
         private IDisposable _itemsCollectionChangedSubscription;
 
@@ -110,10 +117,13 @@ namespace Avalonia.Controls
             set { SetAndRaise(ItemsProperty, ref _items, value); }
         }
 
+        /// <summary>
+        /// Gets the number of items in <see cref="Items"/>.
+        /// </summary>
         public int ItemCount
         {
-            get;
-            private set;
+            get => _itemCount;
+            private set => SetAndRaise(ItemCountProperty, ref _itemCount, value);
         }
 
         /// <summary>

+ 12 - 1
src/Avalonia.Controls/ListBox.cs

@@ -84,6 +84,16 @@ namespace Avalonia.Controls
             set { SetValue(VirtualizationModeProperty, value); }
         }
 
+        /// <summary>
+        /// Selects all items in the <see cref="ListBox"/>.
+        /// </summary>
+        public new void SelectAll() => base.SelectAll();
+
+        /// <summary>
+        /// Deselects all items in the <see cref="ListBox"/>.
+        /// </summary>
+        public new void UnselectAll() => base.UnselectAll();
+
         /// <inheritdoc/>
         protected override IItemContainerGenerator CreateItemContainerGenerator()
         {
@@ -118,7 +128,8 @@ namespace Avalonia.Controls
                     e.Source,
                     true,
                     (e.InputModifiers & InputModifiers.Shift) != 0,
-                    (e.InputModifiers & InputModifiers.Control) != 0);
+                    (e.InputModifiers & InputModifiers.Control) != 0,
+                    e.MouseButton == MouseButton.Right);
             }
         }
 

+ 43 - 22
src/Avalonia.Controls/Primitives/RangeBase.cs

@@ -75,10 +75,18 @@ namespace Avalonia.Controls.Primitives
 
             set
             {
-                value = ValidateMinimum(value);
-                SetAndRaise(MinimumProperty, ref _minimum, value);
-                Maximum = ValidateMaximum(Maximum);
-                Value = ValidateValue(Value);
+                ValidateDouble(value, "Minimum");
+
+                if (IsInitialized)
+                {
+                    SetAndRaise(MinimumProperty, ref _minimum, value);
+                    Maximum = ValidateMaximum(Maximum);
+                    Value = ValidateValue(Value);
+                }
+                else
+                {
+                    SetAndRaise(MinimumProperty, ref _minimum, value);
+                }
             }
         }
 
@@ -94,9 +102,18 @@ namespace Avalonia.Controls.Primitives
 
             set
             {
-                value = ValidateMaximum(value);
-                SetAndRaise(MaximumProperty, ref _maximum, value);
-                Value = ValidateValue(Value);
+                ValidateDouble(value, "Maximum");
+
+                if (IsInitialized)
+                {
+                    value = ValidateMaximum(value);
+                    SetAndRaise(MaximumProperty, ref _maximum, value);
+                    Value = ValidateValue(Value);
+                }
+                else
+                {
+                    SetAndRaise(MaximumProperty, ref _maximum, value);
+                }
             }
         }
 
@@ -112,8 +129,17 @@ namespace Avalonia.Controls.Primitives
 
             set
             {
-                value = ValidateValue(value);
-                SetAndRaise(ValueProperty, ref _value, value);
+                ValidateDouble(value, "Value");
+
+                if (IsInitialized)
+                {
+                    value = ValidateValue(value);
+                    SetAndRaise(ValueProperty, ref _value, value);
+                }
+                else
+                {
+                    SetAndRaise(ValueProperty, ref _value, value);
+                }
             }
         }
 
@@ -129,6 +155,14 @@ namespace Avalonia.Controls.Primitives
             set => SetValue(LargeChangeProperty, value);
         }
 
+        protected override void OnInitialized()
+        {
+            base.OnInitialized();
+
+            Maximum = ValidateMaximum(Maximum);
+            Value = ValidateValue(Value);
+        }
+
         /// <summary>
         /// Throws an exception if the double value is NaN or Inf.
         /// </summary>
@@ -142,17 +176,6 @@ namespace Avalonia.Controls.Primitives
             }
         }
 
-        /// <summary>
-        /// Validates the <see cref="Minimum"/> property.
-        /// </summary>
-        /// <param name="value">The value.</param>
-        /// <returns>The coerced value.</returns>
-        private double ValidateMinimum(double value)
-        {
-            ValidateDouble(value, "Minimum");
-            return value;
-        }
-
         /// <summary>
         /// Validates/coerces the <see cref="Maximum"/> property.
         /// </summary>
@@ -160,7 +183,6 @@ namespace Avalonia.Controls.Primitives
         /// <returns>The coerced value.</returns>
         private double ValidateMaximum(double value)
         {
-            ValidateDouble(value, "Maximum");
             return Math.Max(value, Minimum);
         }
 
@@ -171,7 +193,6 @@ namespace Avalonia.Controls.Primitives
         /// <returns>The coerced value.</returns>
         private double ValidateValue(double value)
         {
-            ValidateDouble(value, "Value");
             return MathUtilities.Clamp(value, Minimum, Maximum);
         }
     }

+ 409 - 235
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@@ -12,6 +12,7 @@ using Avalonia.Data;
 using Avalonia.Input;
 using Avalonia.Input.Platform;
 using Avalonia.Interactivity;
+using Avalonia.Logging;
 using Avalonia.Styling;
 using Avalonia.VisualTree;
 
@@ -103,6 +104,7 @@ namespace Avalonia.Controls.Primitives
 
         private static readonly IList Empty = Array.Empty<object>();
 
+        private readonly Selection _selection = new Selection();
         private int _selectedIndex = -1;
         private object _selectedItem;
         private IList _selectedItems;
@@ -152,23 +154,8 @@ namespace Avalonia.Controls.Primitives
             {
                 if (_updateCount == 0)
                 {
-                    SetAndRaise(SelectedIndexProperty, ref _selectedIndex, (int val, ref int backing, Action<Action> notifyWrapper) =>
-                    {
-                        var old = backing;
-                        var effective = (val >= 0 && val < Items?.Cast<object>().Count()) ? val : -1;
-
-                        if (old != effective)
-                        {
-                            backing = effective;
-                            notifyWrapper(() =>
-                                RaisePropertyChanged(
-                                    SelectedIndexProperty,
-                                    old,
-                                    effective,
-                                    BindingPriority.LocalValue));
-                            SelectedItem = ElementAt(Items, effective);
-                        }
-                    }, value);
+                    var effective = (value >= 0 && value < ItemCount) ? value : -1;
+                    UpdateSelectedItem(effective);
                 }
                 else
                 {
@@ -192,41 +179,7 @@ namespace Avalonia.Controls.Primitives
             {
                 if (_updateCount == 0)
                 {
-                    SetAndRaise(SelectedItemProperty, ref _selectedItem, (object val, ref object backing, Action<Action> notifyWrapper) =>
-                    {
-                        var old = backing;
-                        var index = IndexOf(Items, val);
-                        var effective = index != -1 ? val : null;
-
-                        if (!object.Equals(effective, old))
-                        {
-                            backing = effective;
-
-                            notifyWrapper(() =>
-                                RaisePropertyChanged(
-                                    SelectedItemProperty,
-                                    old,
-                                    effective,
-                                    BindingPriority.LocalValue));
-
-                            SelectedIndex = index;
-
-                            if (effective != null)
-                            {
-                                if (SelectedItems.Count != 1 || SelectedItems[0] != effective)
-                                {
-                                    _syncingSelectedItems = true;
-                                    SelectedItems.Clear();
-                                    SelectedItems.Add(effective);
-                                    _syncingSelectedItems = false;
-                                }
-                            }
-                            else if (SelectedItems.Count > 0)
-                            {
-                                SelectedItems.Clear();
-                            }
-                        }
-                    }, value);
+                    UpdateSelectedItem(IndexOf(Items, value));
                 }
                 else
                 {
@@ -354,31 +307,23 @@ namespace Avalonia.Controls.Primitives
                     {
                         SelectedIndex = 0;
                     }
+                    else
+                    {
+                        _selection.ItemsInserted(e.NewStartingIndex, e.NewItems.Count);
+                        UpdateSelectedItem(_selection.First(), false);
+                    }
 
                     break;
 
                 case NotifyCollectionChangedAction.Remove:
-                case NotifyCollectionChangedAction.Replace:
-                    var selectedIndex = SelectedIndex;
-
-                    if (selectedIndex >= e.OldStartingIndex &&
-                        selectedIndex < e.OldStartingIndex + e.OldItems.Count)
-                    {
-                        if (!AlwaysSelected)
-                        {
-                            selectedIndex = SelectedIndex = -1;
-                        }
-                        else
-                        {
-                            LostSelection();
-                        }
-                    }
+                    _selection.ItemsRemoved(e.OldStartingIndex, e.OldItems.Count);
+                    UpdateSelectedItem(_selection.First(), false);
+                    ResetSelectedItems();
+                    break;
 
-                    var items = Items?.Cast<object>();
-                    if (selectedIndex >= items.Count())
-                    {
-                        selectedIndex = SelectedIndex = items.Count() - 1;
-                    }
+                case NotifyCollectionChangedAction.Replace:
+                    UpdateSelectedItem(SelectedIndex, false);
+                    ResetSelectedItems();
                     break;
 
                 case NotifyCollectionChangedAction.Move:
@@ -439,11 +384,7 @@ namespace Avalonia.Controls.Primitives
             {
                 if (i.ContainerControl != null && i.Item != null)
                 {
-                    var ms = MemberSelector;
-                    bool selected = ms == null ? 
-                        SelectedItems.Contains(i.Item) : 
-                        SelectedItems.OfType<object>().Any(v => Equals(ms.Select(v), i.Item));
-
+                    bool selected = _selection.Contains(i.Index);
                     MarkContainerSelected(i.ContainerControl, selected);
                 }
             }
@@ -476,9 +417,12 @@ namespace Avalonia.Controls.Primitives
                 var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
                 bool Match(List<KeyGesture> gestures) => gestures.Any(g => g.Matches(e));
 
-                if (this.SelectionMode == SelectionMode.Multiple && Match(keymap.SelectAll))
+                if (ItemCount > 0 &&
+                    Match(keymap.SelectAll) &&
+                    (((SelectionMode & SelectionMode.Multiple) != 0) ||
+                      (SelectionMode & SelectionMode.Toggle) != 0))
                 {
-                    SynchronizeItems(SelectedItems, Items?.Cast<object>());
+                    SelectAll();
                     e.Handled = true;
                 }
             }
@@ -520,6 +464,41 @@ namespace Avalonia.Controls.Primitives
             return false;
         }
 
+        /// <summary>
+        /// Selects all items in the control.
+        /// </summary>
+        protected void SelectAll()
+        {
+            if ((SelectionMode & (SelectionMode.Multiple | SelectionMode.Toggle)) == 0)
+            {
+                throw new NotSupportedException("Multiple selection is not enabled on this control.");
+            }
+
+            UpdateSelectedItems(() =>
+            {
+                _selection.Clear();
+
+                for (var i = 0; i < ItemCount; ++i)
+                {
+                    _selection.Add(i);
+                }
+
+                UpdateSelectedItem(0, false);
+
+                foreach (var container in ItemContainerGenerator.Containers)
+                {
+                    MarkItemSelected(container.Index, true);
+                }
+
+                ResetSelectedItems();
+            });
+        }
+
+        /// <summary>
+        /// Deselects all items in the control.
+        /// </summary>
+        protected void UnselectAll() => UpdateSelectedItem(-1);
+
         /// <summary>
         /// Updates the selection for an item based on user interaction.
         /// </summary>
@@ -527,51 +506,83 @@ namespace Avalonia.Controls.Primitives
         /// <param name="select">Whether the item should be selected or unselected.</param>
         /// <param name="rangeModifier">Whether the range modifier is enabled (i.e. shift key).</param>
         /// <param name="toggleModifier">Whether the toggle modifier is enabled (i.e. ctrl key).</param>
+        /// <param name="rightButton">Whether the event is a right-click.</param>
         protected void UpdateSelection(
             int index,
             bool select = true,
             bool rangeModifier = false,
-            bool toggleModifier = false)
+            bool toggleModifier = false,
+            bool rightButton = false)
         {
             if (index != -1)
             {
                 if (select)
                 {
                     var mode = SelectionMode;
-                    var toggle = toggleModifier || (mode & SelectionMode.Toggle) != 0;
                     var multi = (mode & SelectionMode.Multiple) != 0;
-                    var range = multi && SelectedIndex != -1 && rangeModifier;
+                    var toggle = (toggleModifier || (mode & SelectionMode.Toggle) != 0);
+                    var range = multi && rangeModifier;
 
-                    if (!toggle && !range)
+                    if (range)
                     {
-                        SelectedIndex = index;
-                    }
-                    else if (multi && range)
-                    {
-                        SynchronizeItems(
-                            SelectedItems,
-                            GetRange(Items, SelectedIndex, index));
+                        UpdateSelectedItems(() =>
+                        {
+                            var start = SelectedIndex != -1 ? SelectedIndex : 0;
+                            var step = start < index ? 1 : -1;
+
+                            _selection.Clear();
+
+                            for (var i = start; i != index; i += step)
+                            {
+                                _selection.Add(i);
+                            }
+
+                            _selection.Add(index);
+
+                            var first = Math.Min(start, index);
+                            var last = Math.Max(start, index);
+
+                            foreach (var container in ItemContainerGenerator.Containers)
+                            {
+                                MarkItemSelected(
+                                    container.Index,
+                                    container.Index >= first && container.Index <= last);
+                            }
+
+                            ResetSelectedItems();
+                        });
                     }
-                    else
+                    else if (multi && toggle)
                     {
-                        var item = ElementAt(Items, index);
-                        var i = SelectedItems.IndexOf(item);
-
-                        if (i != -1 && (!AlwaysSelected || SelectedItems.Count > 1))
-                        {
-                            SelectedItems.Remove(item);
-                        }
-                        else
+                        UpdateSelectedItems(() =>
                         {
-                            if (multi)
+                            if (!_selection.Contains(index))
                             {
-                                SelectedItems.Add(item);
+                                _selection.Add(index);
+                                MarkItemSelected(index, true);
+                                SelectedItems.Add(ElementAt(Items, index));
                             }
                             else
                             {
-                                SelectedIndex = index;
+                                _selection.Remove(index);
+                                MarkItemSelected(index, false);
+
+                                if (index == _selectedIndex)
+                                {
+                                    UpdateSelectedItem(_selection.First(), false);
+                                }
+
+                                SelectedItems.Remove(ElementAt(Items, index));
                             }
-                        }
+                        });
+                    }
+                    else if (toggle)
+                    {
+                        SelectedIndex = (SelectedIndex == index) ? -1 : index;
+                    }
+                    else
+                    {
+                        UpdateSelectedItem(index, !(rightButton && _selection.Contains(index)));
                     }
 
                     if (Presenter?.Panel != null)
@@ -596,17 +607,19 @@ namespace Avalonia.Controls.Primitives
         /// <param name="select">Whether the container should be selected or unselected.</param>
         /// <param name="rangeModifier">Whether the range modifier is enabled (i.e. shift key).</param>
         /// <param name="toggleModifier">Whether the toggle modifier is enabled (i.e. ctrl key).</param>
+        /// <param name="rightButton">Whether the event is a right-click.</param>
         protected void UpdateSelection(
             IControl container,
             bool select = true,
             bool rangeModifier = false,
-            bool toggleModifier = false)
+            bool toggleModifier = false,
+            bool rightButton = false)
         {
             var index = ItemContainerGenerator?.IndexFromContainer(container) ?? -1;
 
             if (index != -1)
             {
-                UpdateSelection(index, select, rangeModifier, toggleModifier);
+                UpdateSelection(index, select, rangeModifier, toggleModifier, rightButton);
             }
         }
 
@@ -618,6 +631,7 @@ namespace Avalonia.Controls.Primitives
         /// <param name="select">Whether the container should be selected or unselected.</param>
         /// <param name="rangeModifier">Whether the range modifier is enabled (i.e. shift key).</param>
         /// <param name="toggleModifier">Whether the toggle modifier is enabled (i.e. ctrl key).</param>
+        /// <param name="rightButton">Whether the event is a right-click.</param>
         /// <returns>
         /// True if the event originated from a container that belongs to the control; otherwise
         /// false.
@@ -626,51 +640,20 @@ namespace Avalonia.Controls.Primitives
             IInteractive eventSource,
             bool select = true,
             bool rangeModifier = false,
-            bool toggleModifier = false)
+            bool toggleModifier = false,
+            bool rightButton = false)
         {
             var container = GetContainerFromEventSource(eventSource);
 
             if (container != null)
             {
-                UpdateSelection(container, select, rangeModifier, toggleModifier);
+                UpdateSelection(container, select, rangeModifier, toggleModifier, rightButton);
                 return true;
             }
 
             return false;
         }
 
-        /// <summary>
-        /// Makes a list of objects equal another.
-        /// </summary>
-        /// <param name="items">The items collection.</param>
-        /// <param name="desired">The desired items.</param>
-        internal static void SynchronizeItems(IList items, IEnumerable<object> desired)
-        {
-            var index = 0;
-
-            foreach (object item in desired)
-            {
-                int itemIndex = items.IndexOf(item);
-
-                if (itemIndex == -1)
-                {
-                    items.Insert(index, item);
-                }
-                else if(itemIndex != index)
-                {
-                    items.RemoveAt(itemIndex);
-                    items.Insert(index, item);
-                }
-
-                ++index;
-            }
-
-            while (index < items.Count)
-            {
-                items.RemoveAt(items.Count - 1);
-            }
-        }
-
         /// <summary>
         /// Gets a range of items from an IEnumerable.
         /// </summary>
@@ -678,17 +661,19 @@ namespace Avalonia.Controls.Primitives
         /// <param name="first">The index of the first item.</param>
         /// <param name="last">The index of the last item.</param>
         /// <returns>The items.</returns>
-        private static IEnumerable<object> GetRange(IEnumerable items, int first, int last)
+        private static List<object> GetRange(IEnumerable items, int first, int last)
         {
             var list = (items as IList) ?? items.Cast<object>().ToList();
-            int step = first > last ? -1 : 1;
+            var step = first > last ? -1 : 1;
+            var result = new List<object>();
 
             for (int i = first; i != last; i += step)
             {
-                yield return list[i];
+                result.Add(list[i]);
             }
 
-            yield return list[last];
+            result.Add(list[last]);
+            return result;
         }
 
         /// <summary>
@@ -724,19 +709,14 @@ namespace Avalonia.Controls.Primitives
         private void LostSelection()
         {
             var items = Items?.Cast<object>();
+            var index = -1;
 
             if (items != null && AlwaysSelected)
             {
-                var index = Math.Min(SelectedIndex, items.Count() - 1);
-
-                if (index > -1)
-                {
-                    SelectedItem = items.ElementAt(index);
-                    return;
-                }
+                index = Math.Min(SelectedIndex, items.Count() - 1);
             }
 
-            SelectedIndex = -1;
+            SelectedIndex = index;
         }
 
         /// <summary>
@@ -793,7 +773,7 @@ namespace Avalonia.Controls.Primitives
         /// </summary>
         /// <param name="item">The item.</param>
         /// <param name="selected">Whether the item should be selected or deselected.</param>
-        private void MarkItemSelected(object item, bool selected)
+        private int MarkItemSelected(object item, bool selected)
         {
             var index = IndexOf(Items, item);
 
@@ -801,6 +781,21 @@ namespace Avalonia.Controls.Primitives
             {
                 MarkItemSelected(index, selected);
             }
+
+            return index;
+        }
+
+        private void ResetSelectedItems()
+        {
+            UpdateSelectedItems(() =>
+            {
+                SelectedItems.Clear();
+
+                foreach (var i in _selection)
+                {
+                    SelectedItems.Add(ElementAt(Items, i));
+                }
+            });
         }
 
         /// <summary>
@@ -810,95 +805,97 @@ namespace Avalonia.Controls.Primitives
         /// <param name="e">The event args.</param>
         private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
         {
-            var generator = ItemContainerGenerator;
+            if (_syncingSelectedItems)
+            {
+                return;
+            }
+
+            void Add(IList newItems, IList addedItems = null)
+            {
+                foreach (var item in newItems)
+                {
+                    var index = MarkItemSelected(item, true);
+
+                    if (index != -1 && _selection.Add(index) && addedItems != null)
+                    {
+                        addedItems.Add(item);
+                    }
+                }
+            }
+
+            void UpdateSelection()
+            {
+                if ((SelectedIndex != -1 && !_selection.Contains(SelectedIndex)) ||
+                    (SelectedIndex == -1 && _selection.HasItems))
+                {
+                    _selectedIndex = _selection.First();
+                    _selectedItem = ElementAt(Items, _selectedIndex);
+                    RaisePropertyChanged(SelectedIndexProperty, -1, _selectedIndex, BindingPriority.LocalValue);
+                    RaisePropertyChanged(SelectedItemProperty, null, _selectedItem, BindingPriority.LocalValue);
+
+                    if (AutoScrollToSelectedItem)
+                    {
+                        ScrollIntoView(_selectedIndex);
+                    }
+                }
+            }
+
             IList added = null;
             IList removed = null;
 
             switch (e.Action)
             {
                 case NotifyCollectionChangedAction.Add:
-                    SelectedItemsAdded(e.NewItems.Cast<object>().ToList());
-
-                    if (AutoScrollToSelectedItem)
                     {
-                        ScrollIntoView(e.NewItems[0]);
+                        Add(e.NewItems);
+                        UpdateSelection();
+                        added = e.NewItems;
                     }
 
-                    added = e.NewItems;
                     break;
 
                 case NotifyCollectionChangedAction.Remove:
                     if (SelectedItems.Count == 0)
                     {
-                        if (!_syncingSelectedItems)
-                        {
-                            SelectedIndex = -1;
-                        }
+                        SelectedIndex = -1;
                     }
 
                     foreach (var item in e.OldItems)
                     {
-                        MarkItemSelected(item, false);
+                        var index = MarkItemSelected(item, false);
+                        _selection.Remove(index);
                     }
 
                     removed = e.OldItems;
                     break;
 
+                case NotifyCollectionChangedAction.Replace:
+                    throw new NotSupportedException("Replacing items in a SelectedItems collection is not supported.");
+
+                case NotifyCollectionChangedAction.Move:
+                    throw new NotSupportedException("Moving items in a SelectedItems collection is not supported.");
+
                 case NotifyCollectionChangedAction.Reset:
-                    if (generator != null)
                     {
                         removed = new List<object>();
+                        added = new List<object>();
 
-                        foreach (var item in generator.Containers)
+                        foreach (var index in _selection.ToList())
                         {
-                            if (item?.ContainerControl != null)
+                            var item = ElementAt(Items, index);
+
+                            if (!SelectedItems.Contains(item))
                             {
-                                if (MarkContainerSelected(item.ContainerControl, false))
-                                {
-                                    removed.Add(item.Item);
-                                }
+                                MarkItemSelected(index, false);
+                                removed.Add(item);
+                                _selection.Remove(index);
                             }
                         }
-                    }
 
-                    if (SelectedItems.Count > 0)
-                    {
-                        _selectedItem = null;
-                        SelectedItemsAdded(SelectedItems);
-                        added = SelectedItems;
-                    }
-                    else if (!_syncingSelectedItems)
-                    {
-                        SelectedIndex = -1;
-                    }
-
-                    break;
-
-                case NotifyCollectionChangedAction.Replace:
-                    foreach (var item in e.OldItems)
-                    {
-                        MarkItemSelected(item, false);
-                    }
-
-                    foreach (var item in e.NewItems)
-                    {
-                        MarkItemSelected(item, true);
+                        Add(SelectedItems, added);
+                        UpdateSelection();
                     }
 
-                    if (SelectedItem != SelectedItems[0] && !_syncingSelectedItems)
-                    {
-                        var oldItem = SelectedItem;
-                        var oldIndex = SelectedIndex;
-                        var item = SelectedItems[0];
-                        var index = IndexOf(Items, item);
-                        _selectedIndex = index;
-                        _selectedItem = item;
-                        RaisePropertyChanged(SelectedIndexProperty, oldIndex, index, BindingPriority.LocalValue);
-                        RaisePropertyChanged(SelectedItemProperty, oldItem, item, BindingPriority.LocalValue);
-                    }
-
-                    added = e.NewItems;
-                    removed = e.OldItems;
                     break;
             }
 
@@ -912,34 +909,6 @@ namespace Avalonia.Controls.Primitives
             }
         }
 
-        /// <summary>
-        /// Called when items are added to the <see cref="SelectedItems"/> collection.
-        /// </summary>
-        /// <param name="items">The added items.</param>
-        private void SelectedItemsAdded(IList items)
-        {
-            if (items.Count > 0)
-            {
-                foreach (var item in items)
-                {
-                    MarkItemSelected(item, true);
-                }
-
-                if (SelectedItem == null && !_syncingSelectedItems)
-                {
-                    var index = IndexOf(Items, items[0]);
-
-                    if (index != -1)
-                    {
-                        _selectedItem = items[0];
-                        _selectedIndex = index;
-                        RaisePropertyChanged(SelectedIndexProperty, -1, index, BindingPriority.LocalValue);
-                        RaisePropertyChanged(SelectedItemProperty, null, items[0], BindingPriority.LocalValue);
-                    }
-                }
-            }
-        }
-
         /// <summary>
         /// Subscribes to the <see cref="SelectedItems"/> CollectionChanged event, if any.
         /// </summary>
@@ -970,6 +939,112 @@ namespace Avalonia.Controls.Primitives
             }
         }
 
+        /// <summary>
+        /// Updates the selection due to a change to <see cref="SelectedIndex"/> or
+        /// <see cref="SelectedItem"/>.
+        /// </summary>
+        /// <param name="index">The new selected index.</param>
+        /// <param name="clear">Whether to clear existing selection.</param>
+        private void UpdateSelectedItem(int index, bool clear = true)
+        {
+            var oldIndex = _selectedIndex;
+            var oldItem = _selectedItem;
+
+            if (index == -1 && AlwaysSelected)
+            {
+                index = Math.Min(SelectedIndex, ItemCount - 1);
+            }
+
+            var item = ElementAt(Items, index);
+            var added = -1;
+            HashSet<int> removed = null;
+
+            _selectedIndex = index;
+            _selectedItem = item;
+
+            if (oldIndex != index || _selection.HasMultiple)
+            {
+                if (clear)
+                {
+                    removed = _selection.Clear();
+                }
+
+                if (index != -1)
+                {
+                    if (_selection.Add(index))
+                    {
+                        added = index;
+                    }
+
+                    if (removed?.Contains(index) == true)
+                    {
+                        removed.Remove(index);
+                        added = -1;
+                    }
+                }
+
+                if (removed != null)
+                {
+                    foreach (var i in removed)
+                    {
+                        MarkItemSelected(i, false);
+                    }
+                }
+
+                MarkItemSelected(index, true);
+
+                RaisePropertyChanged(
+                    SelectedIndexProperty,
+                    oldIndex,
+                    index);
+            }
+
+            if (!Equals(item, oldItem))
+            {
+                RaisePropertyChanged(
+                    SelectedItemProperty,
+                    oldItem,
+                    item);
+            }
+
+            if (removed != null && index != -1)
+            {
+                removed.Remove(index);
+            }
+
+            if (added != -1 || removed?.Count > 0)
+            {
+                ResetSelectedItems();
+
+                var e = new SelectionChangedEventArgs(
+                    SelectionChangedEvent,
+                    added != -1 ? new[] { ElementAt(Items, added) } : Array.Empty<object>(),
+                    removed?.Select(x => ElementAt(Items, x)).ToArray() ?? Array.Empty<object>());
+                RaiseEvent(e);
+            }
+        }
+
+        private void UpdateSelectedItems(Action action)
+        {
+            try
+            {
+                _syncingSelectedItems = true;
+                action();
+            }
+            catch (Exception ex)
+            {
+                Logger.Error(
+                    LogArea.Property,
+                    this,
+                    "Error thrown updating SelectedItems: {Error}",
+                    ex);
+            }
+            finally
+            {
+                _syncingSelectedItems = false;
+            }
+        }
+
         private void UpdateFinished()
         {
             if (_updateSelectedIndex != int.MinValue)
@@ -981,5 +1056,104 @@ namespace Avalonia.Controls.Primitives
                 SelectedItems = _updateSelectedItems;
             }
         }
+
+        private class Selection : IEnumerable<int>
+        {
+            private readonly List<int> _list = new List<int>();
+            private HashSet<int> _set = new HashSet<int>();
+
+            public bool HasItems => _set.Count > 0;
+            public bool HasMultiple => _set.Count > 1;
+
+            public bool Add(int index)
+            {
+                if (index == -1)
+                {
+                    throw new ArgumentException("Invalid index", "index");
+                }
+
+                if (_set.Add(index))
+                {
+                    _list.Add(index);
+                    return true;
+                }
+
+                return false;
+            }
+
+            public bool Remove(int index)
+            {
+                if (_set.Remove(index))
+                {
+                    _list.RemoveAll(x => x == index);
+                    return true;
+                }
+
+                return false;
+            }
+
+            public HashSet<int> Clear()
+            {
+                var result = _set;
+                _list.Clear();
+                _set = new HashSet<int>();
+                return result;
+            }
+
+            public void ItemsInserted(int index, int count)
+            {
+                _set = new HashSet<int>();
+
+                for (var i = 0; i < _list.Count; ++i)
+                {
+                    var ix = _list[i];
+
+                    if (ix >= index)
+                    {
+                        var newIndex = ix + count;
+                        _list[i] = newIndex;
+                        _set.Add(newIndex);
+                    }
+                    else
+                    {
+                        _set.Add(ix);
+                    }
+                }
+            }
+
+            public void ItemsRemoved(int index, int count)
+            {
+                var last = (index + count) - 1;
+
+                _set = new HashSet<int>();
+
+                for (var i = 0; i < _list.Count; ++i)
+                {
+                    var ix = _list[i];
+
+                    if (ix >= index && ix <= last)
+                    {
+                        _list.RemoveAt(i--);
+                    }
+                    else if (ix > last)
+                    {
+                        var newIndex = ix - count;
+                        _list[i] = newIndex;
+                        _set.Add(newIndex);
+                    }
+                    else
+                    {
+                        _set.Add(ix);
+                    }
+                }
+            }
+
+            public bool Contains(int index) => _set.Contains(index);
+
+            public int First() => HasItems ? _list[0] : -1;
+
+            public IEnumerator<int> GetEnumerator() => _set.GetEnumerator();
+            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+        }
     }
 }

+ 31 - 10
src/Avalonia.Controls/RowDefinition.cs

@@ -29,7 +29,7 @@ namespace Avalonia.Controls
         /// <summary>
         /// Initializes a new instance of the <see cref="RowDefinition"/> class.
         /// </summary>
-        public RowDefinition() 
+        public RowDefinition()
         {
         }
 
@@ -38,7 +38,7 @@ namespace Avalonia.Controls
         /// </summary>
         /// <param name="value">The height of the row.</param>
         /// <param name="type">The height unit of the column.</param>
-        public RowDefinition(double value, GridUnitType type) 
+        public RowDefinition(double value, GridUnitType type)
         {
             Height = new GridLength(value, type);
         }
@@ -47,7 +47,7 @@ namespace Avalonia.Controls
         /// Initializes a new instance of the <see cref="RowDefinition"/> class.
         /// </summary>
         /// <param name="height">The height of the column.</param>
-        public RowDefinition(GridLength height) 
+        public RowDefinition(GridLength height)
         {
             Height = height;
         }
@@ -62,8 +62,15 @@ namespace Avalonia.Controls
         /// </summary>
         public double MaxHeight
         {
-            get { return GetValue(MaxHeightProperty); }
-            set { SetValue(MaxHeightProperty, value); }
+            get
+            {
+                return GetValue(MaxHeightProperty);
+            }
+            set
+            {
+                Parent?.InvalidateMeasure();
+                SetValue(MaxHeightProperty, value);
+            }
         }
 
         /// <summary>
@@ -71,8 +78,15 @@ namespace Avalonia.Controls
         /// </summary>
         public double MinHeight
         {
-            get { return GetValue(MinHeightProperty); }
-            set { SetValue(MinHeightProperty, value); }
+            get
+            {
+                return GetValue(MinHeightProperty);
+            }
+            set
+            {
+                Parent?.InvalidateMeasure();
+                SetValue(MinHeightProperty, value);
+            }
         }
 
         /// <summary>
@@ -80,12 +94,19 @@ namespace Avalonia.Controls
         /// </summary>
         public GridLength Height
         {
-            get { return GetValue(HeightProperty); }
-            set { SetValue(HeightProperty, value); }
+            get
+            {
+                return GetValue(HeightProperty);
+            }
+            set
+            {
+                Parent?.InvalidateMeasure();
+                SetValue(HeightProperty, value);
+            }
         }
 
         internal override GridLength UserSizeValueCache => this.Height;
         internal override double UserMinSizeValueCache => this.MinHeight;
         internal override double UserMaxSizeValueCache => this.MaxHeight;
     }
-}
+}

+ 0 - 36
src/Avalonia.Controls/StartupEventArgs.cs

@@ -1,36 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Contains the arguments for the <see cref="IApplicationLifecycle.Startup"/> event.
-    /// </summary>
-    public class StartupEventArgs : EventArgs
-    {
-        private string[] _args;
-
-        /// <summary>
-        /// Gets the command line arguments that were passed to the application.
-        /// </summary>
-        public IReadOnlyList<string> Args => _args ?? (_args = GetArgs());
-
-        private static string[] GetArgs()
-        {
-            try
-            {
-                var args = Environment.GetCommandLineArgs();
-
-                return args.Length > 1 ? args.Skip(1).ToArray() : new string[0];
-            }
-            catch (NotSupportedException)
-            {
-                return new string[0];
-            }
-        }
-    }
-}

+ 0 - 20
src/Avalonia.Controls/TopLevel.cs

@@ -51,7 +51,6 @@ namespace Avalonia.Controls
         private readonly IInputManager _inputManager;
         private readonly IAccessKeyHandler _accessKeyHandler;
         private readonly IKeyboardNavigationHandler _keyboardNavigationHandler;
-        private readonly IApplicationLifecycle _applicationLifecycle;
         private readonly IPlatformRenderInterface _renderInterface;
         private Size _clientSize;
         private ILayoutManager _layoutManager;
@@ -96,7 +95,6 @@ namespace Avalonia.Controls
             _accessKeyHandler = TryGetService<IAccessKeyHandler>(dependencyResolver);
             _inputManager = TryGetService<IInputManager>(dependencyResolver);
             _keyboardNavigationHandler = TryGetService<IKeyboardNavigationHandler>(dependencyResolver);
-            _applicationLifecycle = TryGetService<IApplicationLifecycle>(dependencyResolver);
             _renderInterface = TryGetService<IPlatformRenderInterface>(dependencyResolver);
 
             Renderer = impl.CreateRenderer(this);
@@ -125,11 +123,6 @@ namespace Avalonia.Controls
                     x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty<Cursor>())
                 .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor));
 
-            if (_applicationLifecycle != null)
-            {
-                _applicationLifecycle.Exit += OnApplicationExiting;
-            }
-
             if (((IStyleHost)this).StylingParent is IResourceProvider applicationResources)
             {
                 WeakSubscriptionManager.Subscribe(
@@ -281,7 +274,6 @@ namespace Avalonia.Controls
             Closed?.Invoke(this, EventArgs.Empty);
             Renderer?.Dispose();
             Renderer = null;
-            _applicationLifecycle.Exit -= OnApplicationExiting;
         }
 
         /// <summary>
@@ -348,18 +340,6 @@ namespace Avalonia.Controls
             return result;
         }
 
-        private void OnApplicationExiting(object sender, EventArgs args)
-        {
-            HandleApplicationExiting();
-        }
-
-        /// <summary>
-        /// Handles the application exiting, either from the last window closing, or a call to <see cref="IApplicationLifecycle.Exit"/>.
-        /// </summary>
-        protected virtual void HandleApplicationExiting()
-        {
-        }
-
         /// <summary>
         /// Handles input from <see cref="ITopLevelImpl.Input"/>.
         /// </summary>

+ 24 - 2
src/Avalonia.Controls/TreeView.cs

@@ -409,7 +409,7 @@ namespace Avalonia.Controls
 
                 if (this.SelectionMode == SelectionMode.Multiple && Match(keymap.SelectAll))
                 {
-                    SelectingItemsControl.SynchronizeItems(SelectedItems, ItemContainerGenerator.Index.Items);
+                    SynchronizeItems(SelectedItems, ItemContainerGenerator.Index.Items);
                     e.Handled = true;
                 }
             }
@@ -521,7 +521,7 @@ namespace Avalonia.Controls
             }
             else if (multi && range)
             {
-                SelectingItemsControl.SynchronizeItems(
+                SynchronizeItems(
                     SelectedItems,
                     GetItemsInRange(selectedContainer as TreeViewItem, container as TreeViewItem));
             }
@@ -778,5 +778,27 @@ namespace Avalonia.Controls
                 container.Classes.Set(":selected", selected);
             }
         }
+
+        /// <summary>
+        /// Makes a list of objects equal another (though doesn't preserve order).
+        /// </summary>
+        /// <param name="items">The items collection.</param>
+        /// <param name="desired">The desired items.</param>
+        private static void SynchronizeItems(IList items, IEnumerable<object> desired)
+        {
+            var list = items.Cast<object>().ToList();
+            var toRemove = list.Except(desired).ToList();
+            var toAdd = desired.Except(list).ToList();
+
+            foreach (var i in toRemove)
+            {
+                items.Remove(i);
+            }
+
+            foreach (var i in toAdd)
+            {
+                items.Add(i);
+            }
+        }
     }
 }

+ 18 - 42
src/Avalonia.Controls/Window.cs

@@ -14,6 +14,7 @@ using System.Collections.Generic;
 using System.Linq;
 using JetBrains.Annotations;
 using System.ComponentModel;
+using Avalonia.Interactivity;
 
 namespace Avalonia.Controls
 {
@@ -97,6 +98,20 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<bool> CanResizeProperty =
             AvaloniaProperty.Register<Window, bool>(nameof(CanResize), true);
 
+        /// <summary>
+        /// Routed event that can be used for global tracking of window destruction
+        /// </summary>
+        public static readonly RoutedEvent WindowClosedEvent =
+            RoutedEvent.Register<Window, RoutedEventArgs>("WindowClosed", RoutingStrategies.Direct);
+        
+        /// <summary>
+        /// Routed event that can be used for global tracking of opening windows
+        /// </summary>
+        public static readonly RoutedEvent WindowOpenedEvent =
+            RoutedEvent.Register<Window, RoutedEventArgs>("WindowOpened", RoutingStrategies.Direct);
+
+
+
         private readonly NameScope _nameScope = new NameScope();
         private object _dialogResult;
         private readonly Size _maxPlatformClientSize;
@@ -249,26 +264,6 @@ namespace Avalonia.Controls
         /// </summary>
         public event EventHandler<CancelEventArgs> Closing;      
 
-        private static void AddWindow(Window window)
-        {
-            if (Application.Current == null)
-            {
-                return;
-            }
-
-            Application.Current.Windows.Add(window);
-        }
-
-        private static void RemoveWindow(Window window)
-        {
-            if (Application.Current == null)
-            {
-                return;
-            }
-
-            Application.Current.Windows.Remove(window);
-        }
-
         /// <summary>
         /// Closes the window.
         /// </summary>
@@ -277,12 +272,6 @@ namespace Avalonia.Controls
             Close(false);
         }
 
-        protected override void HandleApplicationExiting()
-        {
-            base.HandleApplicationExiting();
-            Close(true);
-        }
-
         /// <summary>
         /// Closes a dialog window with the specified result.
         /// </summary>
@@ -382,7 +371,7 @@ namespace Avalonia.Controls
                 return;
             }
 
-            AddWindow(this);
+            this.RaiseEvent(new RoutedEventArgs(WindowOpenedEvent));
 
             EnsureInitialized();
             IsVisible = true;
@@ -444,7 +433,7 @@ namespace Avalonia.Controls
                 throw new InvalidOperationException("The window is already being shown.");
             }
 
-            AddWindow(this);
+            RaiseEvent(new RoutedEventArgs(WindowOpenedEvent));
 
             EnsureInitialized();
             IsVisible = true;
@@ -557,7 +546,7 @@ namespace Avalonia.Controls
 
         protected override void HandleClosed()
         {
-            RemoveWindow(this);
+            RaiseEvent(new RoutedEventArgs(WindowClosedEvent));
 
             base.HandleClosed();
         }
@@ -585,16 +574,3 @@ namespace Avalonia.Controls
         protected virtual void OnClosing(CancelEventArgs e) => Closing?.Invoke(this, e);
     }
 }
-
-namespace Avalonia
-{
-    public static class WindowApplicationExtensions
-    {
-        public static void RunWithMainWindow<TWindow>(this Application app) where TWindow : Avalonia.Controls.Window, new()
-        {
-            var window = new TWindow();
-            window.Show();
-            app.Run(window);
-        }
-    }
-}

+ 0 - 134
src/Avalonia.Controls/WindowCollection.cs

@@ -1,134 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System.Collections;
-using System.Collections.Generic;
-
-using Avalonia.Controls;
-
-namespace Avalonia
-{
-    public class WindowCollection : IReadOnlyList<Window>
-    {
-        private readonly Application _application;
-        private readonly List<Window> _windows = new List<Window>();
-
-        public WindowCollection(Application application)
-        {
-            _application = application;
-        }
-
-        /// <inheritdoc />
-        /// <summary>
-        /// Gets the number of elements in the collection.
-        /// </summary>
-        public int Count => _windows.Count;
-
-        /// <inheritdoc />
-        /// <summary>
-        /// Gets the <see cref="T:Avalonia.Controls.Window" /> at the specified index.
-        /// </summary>
-        /// <value>
-        /// The <see cref="T:Avalonia.Controls.Window" />.
-        /// </value>
-        /// <param name="index">The index.</param>
-        /// <returns></returns>
-        public Window this[int index] => _windows[index];
-
-        /// <inheritdoc />
-        /// <summary>
-        /// Returns an enumerator that iterates through the collection.
-        /// </summary>
-        /// <returns>
-        /// An enumerator that can be used to iterate through the collection.
-        /// </returns>
-        public IEnumerator<Window> GetEnumerator()
-        {
-            return _windows.GetEnumerator();
-        }
-
-        /// <inheritdoc />
-        /// <summary>
-        /// Returns an enumerator that iterates through a collection.
-        /// </summary>
-        /// <returns>
-        /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
-        /// </returns>
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return GetEnumerator();
-        }
-
-        /// <summary>
-        /// Adds the specified window.
-        /// </summary>
-        /// <param name="window">The window.</param>
-        internal void Add(Window window)
-        {
-            if (window == null)
-            {
-                return;
-            }
-
-            _windows.Add(window);
-        }
-
-        /// <summary>
-        /// Removes the specified window.
-        /// </summary>
-        /// <param name="window">The window.</param>
-        internal void Remove(Window window)
-        {
-            if (window == null)
-            {
-                return;
-            }
-
-            _windows.Remove(window);
-
-            OnRemoveWindow(window);
-        }
-
-        /// <summary>
-        /// Closes all windows and removes them from the underlying collection.
-        /// </summary>
-        internal void Clear()
-        {
-            while (_windows.Count > 0)
-            {
-                _windows[0].Close(true);
-            }
-        }
-
-        private void OnRemoveWindow(Window window)
-        {
-            if (window == null)
-            {
-                return;
-            }
-
-            if (_application.IsShuttingDown)
-            {
-                return;
-            }
-
-            switch (_application.ShutdownMode)
-            {
-                case ShutdownMode.OnLastWindowClose:
-                    if (Count == 0)
-                    {
-                        _application.Shutdown();
-                    }
-
-                    break;
-                case ShutdownMode.OnMainWindowClose:
-                    if (window == _application.MainWindow)
-                    {
-                        _application.Shutdown();
-                    }
-
-                    break;                   
-            }
-        }
-    }
-}

+ 4 - 6
src/Avalonia.Controls/WrapPanel.cs

@@ -1,9 +1,7 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
+// This source file is adapted from the Windows Presentation Foundation project. 
+// (https://github.com/dotnet/wpf/) 
+// 
+// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
 
 using Avalonia.Input;
 using Avalonia.Utilities;

+ 2 - 10
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Net;
 using System.Reflection;
+using System.Threading;
 using System.Xml;
 using Avalonia.Controls;
 using Avalonia.Input;
@@ -122,15 +123,6 @@ namespace Avalonia.DesignerSupport.Remote
         }
 
         private const string BuilderMethodName = "BuildAvaloniaApp";
-
-        class NeverClose : ICloseable
-        {
-            public event EventHandler Closed
-            {
-                add {}
-                remove {}
-            }
-        }
         
         public static void Main(string[] cmdline)
         {
@@ -155,7 +147,7 @@ namespace Avalonia.DesignerSupport.Remote
             transport.OnException += (t, e) => Die(e.ToString());
             Log("Sending StartDesignerSessionMessage");
             transport.Send(new StartDesignerSessionMessage {SessionId = args.SessionId});
-            app.Run(new NeverClose());
+            Dispatcher.UIThread.MainLoop(CancellationToken.None);
         }
 
 

+ 9 - 1
src/Avalonia.Styling/StyledElement.cs

@@ -392,6 +392,7 @@ namespace Avalonia
             if (_initCount == 0 && !IsInitialized)
             {
                 IsInitialized = true;
+                OnInitialized();
                 Initialized?.Invoke(this, EventArgs.Empty);
             }
         }
@@ -608,7 +609,14 @@ namespace Avalonia
         protected virtual void OnDataContextEndUpdate()
         {
         }
-        
+
+        /// <summary>
+        /// Called when the control finishes initialization.
+        /// </summary>
+        protected virtual void OnInitialized()
+        {
+        }
+
         private static void DataContextNotifying(IAvaloniaObject o, bool updateStarted)
         {
             if (o is StyledElement element)

+ 3 - 7
src/Avalonia.X11/X11KeyTransform.cs

@@ -221,12 +221,8 @@ namespace Avalonia.X11
             //{ X11Key.?, Key.DeadCharProcessed }
         };
 
-        public static Key ConvertKey(IntPtr key)
-        {
-            var ikey = key.ToInt32();
-            Key result;
-            return KeyDic.TryGetValue((X11Key)ikey, out result) ? result : Key.None;
-        }
-}
+        public static Key ConvertKey(X11Key key) 
+            => KeyDic.TryGetValue(key, out var result) ? result : Key.None;
+    }
     
 }

+ 13 - 2
src/Avalonia.X11/X11Window.cs

@@ -419,10 +419,21 @@ namespace Avalonia.X11
                     return;
                 var buffer = stackalloc byte[40];
 
-                var latinKeysym = XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, 0);
+                var index = ev.KeyEvent.state.HasFlag(XModifierMask.ShiftMask);
+                
+                // We need the latin key, since it's mainly used for hotkeys, we use a different API for text anyway
+                var key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 1 : 0).ToInt32();
+                
+                // Manually switch the Shift index for the keypad,
+                // there should be a proper way to do this
+                if (ev.KeyEvent.state.HasFlag(XModifierMask.Mod2Mask)
+                    && key > X11Key.Num_Lock && key <= X11Key.KP_9)
+                    key = (X11Key)XKeycodeToKeysym(_x11.Display, ev.KeyEvent.keycode, index ? 0 : 1).ToInt32();
+                
+                
                 ScheduleInput(new RawKeyEventArgs(_keyboard, (ulong)ev.KeyEvent.time.ToInt64(),
                     ev.type == XEventName.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
-                    X11KeyTransform.ConvertKey(latinKeysym), TranslateModifiers(ev.KeyEvent.state)), ref ev);
+                    X11KeyTransform.ConvertKey(key), TranslateModifiers(ev.KeyEvent.state)), ref ev);
 
                 if (ev.type == XEventName.KeyPress)
                 {

+ 0 - 11
src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj

@@ -1,11 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-    <DefineConstants>$(DefineConstants);GTK3_PINVOKE</DefineConstants>
-    <PackageId>Avalonia.Gtk3</PackageId>
-  </PropertyGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
-  </ItemGroup>
-</Project>

+ 0 - 51
src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs

@@ -1,51 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Threading.Tasks;
-using Avalonia.Gtk3.Interop;
-using Avalonia.Input.Platform;
-using Avalonia.Platform.Interop;
-
-namespace Avalonia.Gtk3
-{
-    class ClipboardImpl : IClipboard
-    {
-
-        IntPtr GetClipboard() => Native.GtkClipboardGetForDisplay(Native.GdkGetDefaultDisplay(), IntPtr.Zero);
-
-        static void OnText(IntPtr clipboard, IntPtr utf8string, IntPtr userdata)
-        {
-            var handle = GCHandle.FromIntPtr(userdata);
-
-            ((TaskCompletionSource<string>) handle.Target)
-                .TrySetResult(Utf8Buffer.StringFromPtr(utf8string));
-            handle.Free();
-        }
-
-        private static readonly Native.D.GtkClipboardTextReceivedFunc OnTextDelegate = OnText;
-
-        static ClipboardImpl()
-        {
-            GCHandle.Alloc(OnTextDelegate);
-        }
-
-        public Task<string> GetTextAsync()
-        {
-            var tcs = new TaskCompletionSource<string>();
-            Native.GtkClipboardRequestText(GetClipboard(), OnTextDelegate, GCHandle.ToIntPtr(GCHandle.Alloc(tcs)));
-            return tcs.Task;
-        }
-
-        public Task SetTextAsync(string text)
-        {
-            using (var buf = new Utf8Buffer(text))
-                Native.GtkClipboardSetText(GetClipboard(), buf, buf.ByteLen);
-            return Task.FromResult(0);
-        }
-
-        public Task ClearAsync()
-        {
-            Native.GtkClipboardRequestClear(GetClipboard());
-            return Task.FromResult(0);
-        }
-    }
-}

+ 0 - 84
src/Gtk/Avalonia.Gtk3/CursorFactory.cs

@@ -1,84 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Gtk3.Interop;
-using Avalonia.Input;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
-using CursorType = Avalonia.Gtk3.GdkCursorType;
-namespace Avalonia.Gtk3
-{
-    class CursorFactory :  IStandardCursorFactory
-    {
-        private static readonly Dictionary<StandardCursorType, object> CursorTypeMapping = new Dictionary
-    <StandardCursorType, object>
-        {
-            {StandardCursorType.None, CursorType.Blank},
-            {StandardCursorType.AppStarting, CursorType.Watch},
-            {StandardCursorType.Arrow, CursorType.LeftPtr},
-            {StandardCursorType.Cross, CursorType.Cross},
-            {StandardCursorType.Hand, CursorType.Hand1},
-            {StandardCursorType.Ibeam, CursorType.Xterm},
-            {StandardCursorType.No, "gtk-cancel"},
-            {StandardCursorType.SizeAll, CursorType.Sizing},
-            //{ StandardCursorType.SizeNorthEastSouthWest, 32643 },
-            {StandardCursorType.SizeNorthSouth, CursorType.SbVDoubleArrow},
-            //{ StandardCursorType.SizeNorthWestSouthEast, 32642 },
-            {StandardCursorType.SizeWestEast, CursorType.SbHDoubleArrow},
-            {StandardCursorType.UpArrow, CursorType.BasedArrowUp},
-            {StandardCursorType.Wait, CursorType.Watch},
-            {StandardCursorType.Help, "gtk-help"},
-            {StandardCursorType.TopSide, CursorType.TopSide},
-            {StandardCursorType.BottomSize, CursorType.BottomSide},
-            {StandardCursorType.LeftSide, CursorType.LeftSide},
-            {StandardCursorType.RightSide, CursorType.RightSide},
-            {StandardCursorType.TopLeftCorner, CursorType.TopLeftCorner},
-            {StandardCursorType.TopRightCorner, CursorType.TopRightCorner},
-            {StandardCursorType.BottomLeftCorner, CursorType.BottomLeftCorner},
-            {StandardCursorType.BottomRightCorner, CursorType.BottomRightCorner},
-            {StandardCursorType.DragCopy, CursorType.CenterPtr},
-            {StandardCursorType.DragMove, CursorType.Fleur},
-            {StandardCursorType.DragLink, CursorType.Cross},
-        };
-
-        private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache =
-            new Dictionary<StandardCursorType, IPlatformHandle>();
-
-        private IntPtr GetCursor(object desc)
-        {
-            IntPtr rv;
-            var name = desc as string;
-            if (name != null)
-            {
-                var theme = Native.GtkIconThemeGetDefault();
-                IntPtr icon, error;
-                using (var u = new Utf8Buffer(name))
-                    icon = Native.GtkIconThemeLoadIcon(theme, u, 32, 0, out error);
-                rv = icon == IntPtr.Zero
-                    ? Native.GdkCursorNew(GdkCursorType.XCursor)
-                    : Native.GdkCursorNewFromPixbuf(Native.GdkGetDefaultDisplay(), icon, 0, 0);
-            }
-            else
-            {
-                rv = Native.GdkCursorNew((CursorType)desc);
-            }
-
-            
-            return rv;
-        }
-
-        public IPlatformHandle GetCursor(StandardCursorType cursorType)
-        {
-            IPlatformHandle rv;
-            if (!Cache.TryGetValue(cursorType, out rv))
-            {
-                Cache[cursorType] =
-                    rv =
-                        new PlatformHandle(
-                            GetCursor(CursorTypeMapping[cursorType]),
-                            "GTKCURSOR");
-            }
-
-            return rv;
-        }
-    }
-}

+ 0 - 62
src/Gtk/Avalonia.Gtk3/FramebufferManager.cs

@@ -1,62 +0,0 @@
-using System;
-using Avalonia.Controls.Platform.Surfaces;
-using Avalonia.Platform;
-using Avalonia.Threading;
-
-namespace Avalonia.Gtk3
-{
-    class FramebufferManager : IFramebufferPlatformSurface, IDisposable
-    {
-        private readonly WindowBaseImpl _window;
-        public FramebufferManager(WindowBaseImpl window)
-        {
-            _window = window;
-        }
-
-        public void Dispose()
-        {
-            //
-        }
-
-        public ILockedFramebuffer Lock()
-        {
-            // This method may be called from non-UI thread, don't touch anything that calls back to GTK/GDK
-            var s = _window.ClientSize;
-            var width = Math.Max(1, (int) s.Width);
-            var height = Math.Max(1, (int) s.Height);
-            
-            
-            if (!Dispatcher.UIThread.CheckAccess() && Gtk3Platform.DisplayClassName.ToLower().Contains("x11"))
-            {
-                var x11 = LockX11Framebuffer(width, height);
-                if (x11 != null)
-                    return x11;
-            }
-            
-
-            return new ImageSurfaceFramebuffer(_window, width, height, _window.LastKnownScaleFactor);
-        }
-
-        private static int X11ErrorHandler(IntPtr d, ref X11.XErrorEvent e)
-        {
-            return 0;
-        }
-
-        private static X11.XErrorHandler X11ErrorHandlerDelegate = X11ErrorHandler;
-        
-        private static IntPtr X11Display;
-        private ILockedFramebuffer LockX11Framebuffer(int width, int height)
-        {
-            if (!_window.GdkWindowHandle.HasValue)
-                return null;
-            if (X11Display == IntPtr.Zero)
-            {
-                X11Display = X11.XOpenDisplay(IntPtr.Zero);
-                if (X11Display == IntPtr.Zero)
-                    return null;
-                X11.XSetErrorHandler(X11ErrorHandlerDelegate);
-            }
-            return new X11Framebuffer(X11Display, _window.GdkWindowHandle.Value, width, height, _window.LastKnownScaleFactor);
-        }
-    }
-}

+ 0 - 86
src/Gtk/Avalonia.Gtk3/GdkCursor.cs

@@ -1,86 +0,0 @@
-namespace Avalonia.Gtk3
-{
-    enum GdkCursorType
-    {
-        Blank = -2,
-        CursorIsPixmap = -1,
-        XCursor = 0,
-        Arrow = 2,
-        BasedArrowDown = 4,
-        BasedArrowUp = 6,
-        Boat = 8,
-        Bogosity = 10,
-        BottomLeftCorner = 12,
-        BottomRightCorner = 14,
-        BottomSide = 16,
-        BottomTee = 18,
-        BoxSpiral = 20,
-        CenterPtr = 22,
-        Circle = 24,
-        Clock = 26,
-        CoffeeMug = 28,
-        Cross = 30,
-        CrossReverse = 32,
-        Crosshair = 34,
-        DiamondCross = 36,
-        Dot = 38,
-        Dotbox = 40,
-        DoubleArrow = 42,
-        DraftLarge = 44,
-        DraftSmall = 46,
-        DrapedBox = 48,
-        Exchange = 50,
-        Fleur = 52,
-        Gobbler = 54,
-        Gumby = 56,
-        Hand1 = 58,
-        Hand2 = 60,
-        Heart = 62,
-        Icon = 64,
-        IronCross = 66,
-        LeftPtr = 68,
-        LeftSide = 70,
-        LeftTee = 72,
-        Leftbutton = 74,
-        LlAngle = 76,
-        LrAngle = 78,
-        Man = 80,
-        Middlebutton = 82,
-        Mouse = 84,
-        Pencil = 86,
-        Pirate = 88,
-        Plus = 90,
-        QuestionArrow = 92,
-        RightPtr = 94,
-        RightSide = 96,
-        RightTee = 98,
-        Rightbutton = 100,
-        RtlLogo = 102,
-        Sailboat = 104,
-        SbDownArrow = 106,
-        SbHDoubleArrow = 108,
-        SbLeftArrow = 110,
-        SbRightArrow = 112,
-        SbUpArrow = 114,
-        SbVDoubleArrow = 116,
-        Shuttle = 118,
-        Sizing = 120,
-        Spider = 122,
-        Spraycan = 124,
-        Star = 126,
-        Target = 128,
-        Tcross = 130,
-        TopLeftArrow = 132,
-        TopLeftCorner = 134,
-        TopRightCorner = 136,
-        TopSide = 138,
-        TopTee = 140,
-        Trek = 142,
-        UlAngle = 144,
-        Umbrella = 146,
-        UrAngle = 148,
-        Watch = 150,
-        Xterm = 152,
-        LastCursor = 153,
-    }
-}

+ 0 - 1341
src/Gtk/Avalonia.Gtk3/GdkKey.cs

@@ -1,1341 +0,0 @@
-namespace Avalonia.Gtk3
-{
-    enum GdkKey
-    {
-        space = 32,
-        exclam = 33,
-        quotedbl = 34,
-        numbersign = 35,
-        dollar = 36,
-        percent = 37,
-        ampersand = 38,
-        apostrophe = 39,
-        quoteright = 39,
-        parenleft = 40,
-        parenright = 41,
-        asterisk = 42,
-        plus = 43,
-        comma = 44,
-        minus = 45,
-        period = 46,
-        slash = 47,
-        Key_0 = 48,
-        Key_1 = 49,
-        Key_2 = 50,
-        Key_3 = 51,
-        Key_4 = 52,
-        Key_5 = 53,
-        Key_6 = 54,
-        Key_7 = 55,
-        Key_8 = 56,
-        Key_9 = 57,
-        colon = 58,
-        semicolon = 59,
-        less = 60,
-        equal = 61,
-        greater = 62,
-        question = 63,
-        at = 64,
-        A = 65,
-        B = 66,
-        C = 67,
-        D = 68,
-        E = 69,
-        F = 70,
-        G = 71,
-        H = 72,
-        I = 73,
-        J = 74,
-        K = 75,
-        L = 76,
-        M = 77,
-        N = 78,
-        O = 79,
-        P = 80,
-        Q = 81,
-        R = 82,
-        S = 83,
-        T = 84,
-        U = 85,
-        V = 86,
-        W = 87,
-        X = 88,
-        Y = 89,
-        Z = 90,
-        bracketleft = 91,
-        backslash = 92,
-        bracketright = 93,
-        asciicircum = 94,
-        underscore = 95,
-        grave = 96,
-        quoteleft = 96,
-        a = 97,
-        b = 98,
-        c = 99,
-        d = 100,
-        e = 101,
-        f = 102,
-        g = 103,
-        h = 104,
-        i = 105,
-        j = 106,
-        k = 107,
-        l = 108,
-        m = 109,
-        n = 110,
-        o = 111,
-        p = 112,
-        q = 113,
-        r = 114,
-        s = 115,
-        t = 116,
-        u = 117,
-        v = 118,
-        w = 119,
-        x = 120,
-        y = 121,
-        z = 122,
-        braceleft = 123,
-        bar = 124,
-        braceright = 125,
-        asciitilde = 126,
-        nobreakspace = 160,
-        exclamdown = 161,
-        cent = 162,
-        sterling = 163,
-        currency = 164,
-        yen = 165,
-        brokenbar = 166,
-        section = 167,
-        diaeresis = 168,
-        copyright = 169,
-        ordfeminine = 170,
-        guillemotleft = 171,
-        notsign = 172,
-        hyphen = 173,
-        registered = 174,
-        macron = 175,
-        degree = 176,
-        plusminus = 177,
-        twosuperior = 178,
-        threesuperior = 179,
-        acute = 180,
-        mu = 181,
-        paragraph = 182,
-        periodcentered = 183,
-        cedilla = 184,
-        onesuperior = 185,
-        masculine = 186,
-        guillemotright = 187,
-        onequarter = 188,
-        onehalf = 189,
-        threequarters = 190,
-        questiondown = 191,
-        Agrave = 192,
-        Aacute = 193,
-        Acircumflex = 194,
-        Atilde = 195,
-        Adiaeresis = 196,
-        Aring = 197,
-        AE = 198,
-        Ccedilla = 199,
-        Egrave = 200,
-        Eacute = 201,
-        Ecircumflex = 202,
-        Ediaeresis = 203,
-        Igrave = 204,
-        Iacute = 205,
-        Icircumflex = 206,
-        Idiaeresis = 207,
-        ETH = 208,
-        Eth = 208,
-        Ntilde = 209,
-        Ograve = 210,
-        Oacute = 211,
-        Ocircumflex = 212,
-        Otilde = 213,
-        Odiaeresis = 214,
-        multiply = 215,
-        Ooblique = 216,
-        Ugrave = 217,
-        Uacute = 218,
-        Ucircumflex = 219,
-        Udiaeresis = 220,
-        Yacute = 221,
-        THORN = 222,
-        Thorn = 222,
-        ssharp = 223,
-        agrave = 224,
-        aacute = 225,
-        acircumflex = 226,
-        atilde = 227,
-        adiaeresis = 228,
-        aring = 229,
-        ae = 230,
-        ccedilla = 231,
-        egrave = 232,
-        eacute = 233,
-        ecircumflex = 234,
-        ediaeresis = 235,
-        igrave = 236,
-        iacute = 237,
-        icircumflex = 238,
-        idiaeresis = 239,
-        eth = 240,
-        ntilde = 241,
-        ograve = 242,
-        oacute = 243,
-        ocircumflex = 244,
-        otilde = 245,
-        odiaeresis = 246,
-        division = 247,
-        oslash = 248,
-        ugrave = 249,
-        uacute = 250,
-        ucircumflex = 251,
-        udiaeresis = 252,
-        yacute = 253,
-        thorn = 254,
-        ydiaeresis = 255,
-        Aogonek = 417,
-        breve = 418,
-        Lstroke = 419,
-        Lcaron = 421,
-        Sacute = 422,
-        Scaron = 425,
-        Scedilla = 426,
-        Tcaron = 427,
-        Zacute = 428,
-        Zcaron = 430,
-        Zabovedot = 431,
-        aogonek = 433,
-        ogonek = 434,
-        lstroke = 435,
-        lcaron = 437,
-        sacute = 438,
-        caron = 439,
-        scaron = 441,
-        scedilla = 442,
-        tcaron = 443,
-        zacute = 444,
-        doubleacute = 445,
-        zcaron = 446,
-        zabovedot = 447,
-        Racute = 448,
-        Abreve = 451,
-        Lacute = 453,
-        Cacute = 454,
-        Ccaron = 456,
-        Eogonek = 458,
-        Ecaron = 460,
-        Dcaron = 463,
-        Dstroke = 464,
-        Nacute = 465,
-        Ncaron = 466,
-        Odoubleacute = 469,
-        Rcaron = 472,
-        Uring = 473,
-        Udoubleacute = 475,
-        Tcedilla = 478,
-        racute = 480,
-        abreve = 483,
-        lacute = 485,
-        cacute = 486,
-        ccaron = 488,
-        eogonek = 490,
-        ecaron = 492,
-        dcaron = 495,
-        dstroke = 496,
-        nacute = 497,
-        ncaron = 498,
-        odoubleacute = 501,
-        rcaron = 504,
-        uring = 505,
-        udoubleacute = 507,
-        tcedilla = 510,
-        abovedot = 511,
-        Hstroke = 673,
-        Hcircumflex = 678,
-        Iabovedot = 681,
-        Gbreve = 683,
-        Jcircumflex = 684,
-        hstroke = 689,
-        hcircumflex = 694,
-        idotless = 697,
-        gbreve = 699,
-        jcircumflex = 700,
-        Cabovedot = 709,
-        Ccircumflex = 710,
-        Gabovedot = 725,
-        Gcircumflex = 728,
-        Ubreve = 733,
-        Scircumflex = 734,
-        cabovedot = 741,
-        ccircumflex = 742,
-        gabovedot = 757,
-        gcircumflex = 760,
-        ubreve = 765,
-        scircumflex = 766,
-        kappa = 930,
-        kra = 930,
-        Rcedilla = 931,
-        Itilde = 933,
-        Lcedilla = 934,
-        Emacron = 938,
-        Gcedilla = 939,
-        Tslash = 940,
-        rcedilla = 947,
-        itilde = 949,
-        lcedilla = 950,
-        emacron = 954,
-        gcedilla = 955,
-        tslash = 956,
-        ENG = 957,
-        eng = 959,
-        Amacron = 960,
-        Iogonek = 967,
-        Eabovedot = 972,
-        Imacron = 975,
-        Ncedilla = 977,
-        Omacron = 978,
-        Kcedilla = 979,
-        Uogonek = 985,
-        Utilde = 989,
-        Umacron = 990,
-        amacron = 992,
-        iogonek = 999,
-        eabovedot = 1004,
-        imacron = 1007,
-        ncedilla = 1009,
-        omacron = 1010,
-        kcedilla = 1011,
-        uogonek = 1017,
-        utilde = 1021,
-        umacron = 1022,
-        overline = 1150,
-        kana_fullstop = 1185,
-        kana_openingbracket = 1186,
-        kana_closingbracket = 1187,
-        kana_comma = 1188,
-        kana_conjunctive = 1189,
-        kana_middledot = 1189,
-        kana_WO = 1190,
-        kana_a = 1191,
-        kana_i = 1192,
-        kana_u = 1193,
-        kana_e = 1194,
-        kana_o = 1195,
-        kana_ya = 1196,
-        kana_yu = 1197,
-        kana_yo = 1198,
-        kana_tsu = 1199,
-        kana_tu = 1199,
-        prolongedsound = 1200,
-        kana_A = 1201,
-        kana_I = 1202,
-        kana_U = 1203,
-        kana_E = 1204,
-        kana_O = 1205,
-        kana_KA = 1206,
-        kana_KI = 1207,
-        kana_KU = 1208,
-        kana_KE = 1209,
-        kana_KO = 1210,
-        kana_SA = 1211,
-        kana_SHI = 1212,
-        kana_SU = 1213,
-        kana_SE = 1214,
-        kana_SO = 1215,
-        kana_TA = 1216,
-        kana_CHI = 1217,
-        kana_TI = 1217,
-        kana_TSU = 1218,
-        kana_TU = 1218,
-        kana_TE = 1219,
-        kana_TO = 1220,
-        kana_NA = 1221,
-        kana_NI = 1222,
-        kana_NU = 1223,
-        kana_NE = 1224,
-        kana_NO = 1225,
-        kana_HA = 1226,
-        kana_HI = 1227,
-        kana_FU = 1228,
-        kana_HU = 1228,
-        kana_HE = 1229,
-        kana_HO = 1230,
-        kana_MA = 1231,
-        kana_MI = 1232,
-        kana_MU = 1233,
-        kana_ME = 1234,
-        kana_MO = 1235,
-        kana_YA = 1236,
-        kana_YU = 1237,
-        kana_YO = 1238,
-        kana_RA = 1239,
-        kana_RI = 1240,
-        kana_RU = 1241,
-        kana_RE = 1242,
-        kana_RO = 1243,
-        kana_WA = 1244,
-        kana_N = 1245,
-        voicedsound = 1246,
-        semivoicedsound = 1247,
-        Arabic_comma = 1452,
-        Arabic_semicolon = 1467,
-        Arabic_question_mark = 1471,
-        Arabic_hamza = 1473,
-        Arabic_maddaonalef = 1474,
-        Arabic_hamzaonalef = 1475,
-        Arabic_hamzaonwaw = 1476,
-        Arabic_hamzaunderalef = 1477,
-        Arabic_hamzaonyeh = 1478,
-        Arabic_alef = 1479,
-        Arabic_beh = 1480,
-        Arabic_tehmarbuta = 1481,
-        Arabic_teh = 1482,
-        Arabic_theh = 1483,
-        Arabic_jeem = 1484,
-        Arabic_hah = 1485,
-        Arabic_khah = 1486,
-        Arabic_dal = 1487,
-        Arabic_thal = 1488,
-        Arabic_ra = 1489,
-        Arabic_zain = 1490,
-        Arabic_seen = 1491,
-        Arabic_sheen = 1492,
-        Arabic_sad = 1493,
-        Arabic_dad = 1494,
-        Arabic_tah = 1495,
-        Arabic_zah = 1496,
-        Arabic_ain = 1497,
-        Arabic_ghain = 1498,
-        Arabic_tatweel = 1504,
-        Arabic_feh = 1505,
-        Arabic_qaf = 1506,
-        Arabic_kaf = 1507,
-        Arabic_lam = 1508,
-        Arabic_meem = 1509,
-        Arabic_noon = 1510,
-        Arabic_ha = 1511,
-        Arabic_heh = 1511,
-        Arabic_waw = 1512,
-        Arabic_alefmaksura = 1513,
-        Arabic_yeh = 1514,
-        Arabic_fathatan = 1515,
-        Arabic_dammatan = 1516,
-        Arabic_kasratan = 1517,
-        Arabic_fatha = 1518,
-        Arabic_damma = 1519,
-        Arabic_kasra = 1520,
-        Arabic_shadda = 1521,
-        Arabic_sukun = 1522,
-        Serbian_dje = 1697,
-        Macedonia_gje = 1698,
-        Cyrillic_io = 1699,
-        Ukrainian_ie = 1700,
-        Ukranian_je = 1700,
-        Macedonia_dse = 1701,
-        Ukrainian_i = 1702,
-        Ukranian_i = 1702,
-        Ukrainian_yi = 1703,
-        Ukranian_yi = 1703,
-        Cyrillic_je = 1704,
-        Serbian_je = 1704,
-        Cyrillic_lje = 1705,
-        Serbian_lje = 1705,
-        Cyrillic_nje = 1706,
-        Serbian_nje = 1706,
-        Serbian_tshe = 1707,
-        Macedonia_kje = 1708,
-        Byelorussian_shortu = 1710,
-        Cyrillic_dzhe = 1711,
-        Serbian_dze = 1711,
-        numerosign = 1712,
-        Serbian_DJE = 1713,
-        Macedonia_GJE = 1714,
-        Cyrillic_IO = 1715,
-        Ukrainian_IE = 1716,
-        Ukranian_JE = 1716,
-        Macedonia_DSE = 1717,
-        Ukrainian_I = 1718,
-        Ukranian_I = 1718,
-        Ukrainian_YI = 1719,
-        Ukranian_YI = 1719,
-        Cyrillic_JE = 1720,
-        Serbian_JE = 1720,
-        Cyrillic_LJE = 1721,
-        Serbian_LJE = 1721,
-        Cyrillic_NJE = 1722,
-        Serbian_NJE = 1722,
-        Serbian_TSHE = 1723,
-        Macedonia_KJE = 1724,
-        Byelorussian_SHORTU = 1726,
-        Cyrillic_DZHE = 1727,
-        Serbian_DZE = 1727,
-        Cyrillic_yu = 1728,
-        Cyrillic_a = 1729,
-        Cyrillic_be = 1730,
-        Cyrillic_tse = 1731,
-        Cyrillic_de = 1732,
-        Cyrillic_ie = 1733,
-        Cyrillic_ef = 1734,
-        Cyrillic_ghe = 1735,
-        Cyrillic_ha = 1736,
-        Cyrillic_i = 1737,
-        Cyrillic_shorti = 1738,
-        Cyrillic_ka = 1739,
-        Cyrillic_el = 1740,
-        Cyrillic_em = 1741,
-        Cyrillic_en = 1742,
-        Cyrillic_o = 1743,
-        Cyrillic_pe = 1744,
-        Cyrillic_ya = 1745,
-        Cyrillic_er = 1746,
-        Cyrillic_es = 1747,
-        Cyrillic_te = 1748,
-        Cyrillic_u = 1749,
-        Cyrillic_zhe = 1750,
-        Cyrillic_ve = 1751,
-        Cyrillic_softsign = 1752,
-        Cyrillic_yeru = 1753,
-        Cyrillic_ze = 1754,
-        Cyrillic_sha = 1755,
-        Cyrillic_e = 1756,
-        Cyrillic_shcha = 1757,
-        Cyrillic_che = 1758,
-        Cyrillic_hardsign = 1759,
-        Cyrillic_YU = 1760,
-        Cyrillic_A = 1761,
-        Cyrillic_BE = 1762,
-        Cyrillic_TSE = 1763,
-        Cyrillic_DE = 1764,
-        Cyrillic_IE = 1765,
-        Cyrillic_EF = 1766,
-        Cyrillic_GHE = 1767,
-        Cyrillic_HA = 1768,
-        Cyrillic_I = 1769,
-        Cyrillic_SHORTI = 1770,
-        Cyrillic_KA = 1771,
-        Cyrillic_EL = 1772,
-        Cyrillic_EM = 1773,
-        Cyrillic_EN = 1774,
-        Cyrillic_O = 1775,
-        Cyrillic_PE = 1776,
-        Cyrillic_YA = 1777,
-        Cyrillic_ER = 1778,
-        Cyrillic_ES = 1779,
-        Cyrillic_TE = 1780,
-        Cyrillic_U = 1781,
-        Cyrillic_ZHE = 1782,
-        Cyrillic_VE = 1783,
-        Cyrillic_SOFTSIGN = 1784,
-        Cyrillic_YERU = 1785,
-        Cyrillic_ZE = 1786,
-        Cyrillic_SHA = 1787,
-        Cyrillic_E = 1788,
-        Cyrillic_SHCHA = 1789,
-        Cyrillic_CHE = 1790,
-        Cyrillic_HARDSIGN = 1791,
-        Greek_ALPHAaccent = 1953,
-        Greek_EPSILONaccent = 1954,
-        Greek_ETAaccent = 1955,
-        Greek_IOTAaccent = 1956,
-        Greek_IOTAdiaeresis = 1957,
-        Greek_OMICRONaccent = 1959,
-        Greek_UPSILONaccent = 1960,
-        Greek_UPSILONdieresis = 1961,
-        Greek_OMEGAaccent = 1963,
-        Greek_accentdieresis = 1966,
-        Greek_horizbar = 1967,
-        Greek_alphaaccent = 1969,
-        Greek_epsilonaccent = 1970,
-        Greek_etaaccent = 1971,
-        Greek_iotaaccent = 1972,
-        Greek_iotadieresis = 1973,
-        Greek_iotaaccentdieresis = 1974,
-        Greek_omicronaccent = 1975,
-        Greek_upsilonaccent = 1976,
-        Greek_upsilondieresis = 1977,
-        Greek_upsilonaccentdieresis = 1978,
-        Greek_omegaaccent = 1979,
-        Greek_ALPHA = 1985,
-        Greek_BETA = 1986,
-        Greek_GAMMA = 1987,
-        Greek_DELTA = 1988,
-        Greek_EPSILON = 1989,
-        Greek_ZETA = 1990,
-        Greek_ETA = 1991,
-        Greek_THETA = 1992,
-        Greek_IOTA = 1993,
-        Greek_KAPPA = 1994,
-        Greek_LAMBDA = 1995,
-        Greek_LAMDA = 1995,
-        Greek_MU = 1996,
-        Greek_NU = 1997,
-        Greek_XI = 1998,
-        Greek_OMICRON = 1999,
-        Greek_PI = 2000,
-        Greek_RHO = 2001,
-        Greek_SIGMA = 2002,
-        Greek_TAU = 2004,
-        Greek_UPSILON = 2005,
-        Greek_PHI = 2006,
-        Greek_CHI = 2007,
-        Greek_PSI = 2008,
-        Greek_OMEGA = 2009,
-        Greek_alpha = 2017,
-        Greek_beta = 2018,
-        Greek_gamma = 2019,
-        Greek_delta = 2020,
-        Greek_epsilon = 2021,
-        Greek_zeta = 2022,
-        Greek_eta = 2023,
-        Greek_theta = 2024,
-        Greek_iota = 2025,
-        Greek_kappa = 2026,
-        Greek_lambda = 2027,
-        Greek_lamda = 2027,
-        Greek_mu = 2028,
-        Greek_nu = 2029,
-        Greek_xi = 2030,
-        Greek_omicron = 2031,
-        Greek_pi = 2032,
-        Greek_rho = 2033,
-        Greek_sigma = 2034,
-        Greek_finalsmallsigma = 2035,
-        Greek_tau = 2036,
-        Greek_upsilon = 2037,
-        Greek_phi = 2038,
-        Greek_chi = 2039,
-        Greek_psi = 2040,
-        Greek_omega = 2041,
-        leftradical = 2209,
-        topleftradical = 2210,
-        horizconnector = 2211,
-        topintegral = 2212,
-        botintegral = 2213,
-        vertconnector = 2214,
-        topleftsqbracket = 2215,
-        botleftsqbracket = 2216,
-        toprightsqbracket = 2217,
-        botrightsqbracket = 2218,
-        topleftparens = 2219,
-        botleftparens = 2220,
-        toprightparens = 2221,
-        botrightparens = 2222,
-        leftmiddlecurlybrace = 2223,
-        rightmiddlecurlybrace = 2224,
-        topleftsummation = 2225,
-        botleftsummation = 2226,
-        topvertsummationconnector = 2227,
-        botvertsummationconnector = 2228,
-        toprightsummation = 2229,
-        botrightsummation = 2230,
-        rightmiddlesummation = 2231,
-        lessthanequal = 2236,
-        notequal = 2237,
-        greaterthanequal = 2238,
-        integral = 2239,
-        therefore = 2240,
-        variation = 2241,
-        infinity = 2242,
-        nabla = 2245,
-        approximate = 2248,
-        similarequal = 2249,
-        ifonlyif = 2253,
-        implies = 2254,
-        identical = 2255,
-        radical = 2262,
-        includedin = 2266,
-        includes = 2267,
-        intersection = 2268,
-        union = 2269,
-        logicaland = 2270,
-        logicalor = 2271,
-        partialderivative = 2287,
-        function = 2294,
-        leftarrow = 2299,
-        uparrow = 2300,
-        rightarrow = 2301,
-        downarrow = 2302,
-        blank = 2527,
-        soliddiamond = 2528,
-        checkerboard = 2529,
-        ht = 2530,
-        ff = 2531,
-        cr = 2532,
-        lf = 2533,
-        nl = 2536,
-        vt = 2537,
-        lowrightcorner = 2538,
-        uprightcorner = 2539,
-        upleftcorner = 2540,
-        lowleftcorner = 2541,
-        crossinglines = 2542,
-        horizlinescan1 = 2543,
-        horizlinescan3 = 2544,
-        horizlinescan5 = 2545,
-        horizlinescan7 = 2546,
-        horizlinescan9 = 2547,
-        leftt = 2548,
-        rightt = 2549,
-        bott = 2550,
-        topt = 2551,
-        vertbar = 2552,
-        emspace = 2721,
-        enspace = 2722,
-        em3space = 2723,
-        em4space = 2724,
-        digitspace = 2725,
-        punctspace = 2726,
-        thinspace = 2727,
-        hairspace = 2728,
-        emdash = 2729,
-        endash = 2730,
-        signifblank = 2732,
-        ellipsis = 2734,
-        doubbaselinedot = 2735,
-        onethird = 2736,
-        twothirds = 2737,
-        onefifth = 2738,
-        twofifths = 2739,
-        threefifths = 2740,
-        fourfifths = 2741,
-        onesixth = 2742,
-        fivesixths = 2743,
-        careof = 2744,
-        figdash = 2747,
-        leftanglebracket = 2748,
-        decimalpoint = 2749,
-        rightanglebracket = 2750,
-        marker = 2751,
-        oneeighth = 2755,
-        threeeighths = 2756,
-        fiveeighths = 2757,
-        seveneighths = 2758,
-        trademark = 2761,
-        signaturemark = 2762,
-        trademarkincircle = 2763,
-        leftopentriangle = 2764,
-        rightopentriangle = 2765,
-        emopencircle = 2766,
-        emopenrectangle = 2767,
-        leftsinglequotemark = 2768,
-        rightsinglequotemark = 2769,
-        leftdoublequotemark = 2770,
-        rightdoublequotemark = 2771,
-        prescription = 2772,
-        minutes = 2774,
-        seconds = 2775,
-        latincross = 2777,
-        hexagram = 2778,
-        filledrectbullet = 2779,
-        filledlefttribullet = 2780,
-        filledrighttribullet = 2781,
-        emfilledcircle = 2782,
-        emfilledrect = 2783,
-        enopencircbullet = 2784,
-        enopensquarebullet = 2785,
-        openrectbullet = 2786,
-        opentribulletup = 2787,
-        opentribulletdown = 2788,
-        openstar = 2789,
-        enfilledcircbullet = 2790,
-        enfilledsqbullet = 2791,
-        filledtribulletup = 2792,
-        filledtribulletdown = 2793,
-        leftpointer = 2794,
-        rightpointer = 2795,
-        club = 2796,
-        diamond = 2797,
-        heart = 2798,
-        maltesecross = 2800,
-        dagger = 2801,
-        doubledagger = 2802,
-        checkmark = 2803,
-        ballotcross = 2804,
-        musicalsharp = 2805,
-        musicalflat = 2806,
-        malesymbol = 2807,
-        femalesymbol = 2808,
-        telephone = 2809,
-        telephonerecorder = 2810,
-        phonographcopyright = 2811,
-        caret = 2812,
-        singlelowquotemark = 2813,
-        doublelowquotemark = 2814,
-        cursor = 2815,
-        leftcaret = 2979,
-        rightcaret = 2982,
-        downcaret = 2984,
-        upcaret = 2985,
-        overbar = 3008,
-        downtack = 3010,
-        upshoe = 3011,
-        downstile = 3012,
-        underbar = 3014,
-        jot = 3018,
-        quad = 3020,
-        uptack = 3022,
-        circle = 3023,
-        upstile = 3027,
-        downshoe = 3030,
-        rightshoe = 3032,
-        leftshoe = 3034,
-        lefttack = 3036,
-        righttack = 3068,
-        hebrew_doublelowline = 3295,
-        hebrew_aleph = 3296,
-        hebrew_bet = 3297,
-        hebrew_beth = 3297,
-        hebrew_gimel = 3298,
-        hebrew_gimmel = 3298,
-        hebrew_dalet = 3299,
-        hebrew_daleth = 3299,
-        hebrew_he = 3300,
-        hebrew_waw = 3301,
-        hebrew_zain = 3302,
-        hebrew_zayin = 3302,
-        hebrew_chet = 3303,
-        hebrew_het = 3303,
-        hebrew_tet = 3304,
-        hebrew_teth = 3304,
-        hebrew_yod = 3305,
-        hebrew_finalkaph = 3306,
-        hebrew_kaph = 3307,
-        hebrew_lamed = 3308,
-        hebrew_finalmem = 3309,
-        hebrew_mem = 3310,
-        hebrew_finalnun = 3311,
-        hebrew_nun = 3312,
-        hebrew_samech = 3313,
-        hebrew_samekh = 3313,
-        hebrew_ayin = 3314,
-        hebrew_finalpe = 3315,
-        hebrew_pe = 3316,
-        hebrew_finalzade = 3317,
-        hebrew_finalzadi = 3317,
-        hebrew_zade = 3318,
-        hebrew_zadi = 3318,
-        hebrew_kuf = 3319,
-        hebrew_qoph = 3319,
-        hebrew_resh = 3320,
-        hebrew_shin = 3321,
-        hebrew_taf = 3322,
-        hebrew_taw = 3322,
-        Thai_kokai = 3489,
-        Thai_khokhai = 3490,
-        Thai_khokhuat = 3491,
-        Thai_khokhwai = 3492,
-        Thai_khokhon = 3493,
-        Thai_khorakhang = 3494,
-        Thai_ngongu = 3495,
-        Thai_chochan = 3496,
-        Thai_choching = 3497,
-        Thai_chochang = 3498,
-        Thai_soso = 3499,
-        Thai_chochoe = 3500,
-        Thai_yoying = 3501,
-        Thai_dochada = 3502,
-        Thai_topatak = 3503,
-        Thai_thothan = 3504,
-        Thai_thonangmontho = 3505,
-        Thai_thophuthao = 3506,
-        Thai_nonen = 3507,
-        Thai_dodek = 3508,
-        Thai_totao = 3509,
-        Thai_thothung = 3510,
-        Thai_thothahan = 3511,
-        Thai_thothong = 3512,
-        Thai_nonu = 3513,
-        Thai_bobaimai = 3514,
-        Thai_popla = 3515,
-        Thai_phophung = 3516,
-        Thai_fofa = 3517,
-        Thai_phophan = 3518,
-        Thai_fofan = 3519,
-        Thai_phosamphao = 3520,
-        Thai_moma = 3521,
-        Thai_yoyak = 3522,
-        Thai_rorua = 3523,
-        Thai_ru = 3524,
-        Thai_loling = 3525,
-        Thai_lu = 3526,
-        Thai_wowaen = 3527,
-        Thai_sosala = 3528,
-        Thai_sorusi = 3529,
-        Thai_sosua = 3530,
-        Thai_hohip = 3531,
-        Thai_lochula = 3532,
-        Thai_oang = 3533,
-        Thai_honokhuk = 3534,
-        Thai_paiyannoi = 3535,
-        Thai_saraa = 3536,
-        Thai_maihanakat = 3537,
-        Thai_saraaa = 3538,
-        Thai_saraam = 3539,
-        Thai_sarai = 3540,
-        Thai_saraii = 3541,
-        Thai_saraue = 3542,
-        Thai_sarauee = 3543,
-        Thai_sarau = 3544,
-        Thai_sarauu = 3545,
-        Thai_phinthu = 3546,
-        Thai_maihanakat_maitho = 3550,
-        Thai_baht = 3551,
-        Thai_sarae = 3552,
-        Thai_saraae = 3553,
-        Thai_sarao = 3554,
-        Thai_saraaimaimuan = 3555,
-        Thai_saraaimaimalai = 3556,
-        Thai_lakkhangyao = 3557,
-        Thai_maiyamok = 3558,
-        Thai_maitaikhu = 3559,
-        Thai_maiek = 3560,
-        Thai_maitho = 3561,
-        Thai_maitri = 3562,
-        Thai_maichattawa = 3563,
-        Thai_thanthakhat = 3564,
-        Thai_nikhahit = 3565,
-        Thai_leksun = 3568,
-        Thai_leknung = 3569,
-        Thai_leksong = 3570,
-        Thai_leksam = 3571,
-        Thai_leksi = 3572,
-        Thai_lekha = 3573,
-        Thai_lekhok = 3574,
-        Thai_lekchet = 3575,
-        Thai_lekpaet = 3576,
-        Thai_lekkao = 3577,
-        Hangul_Kiyeog = 3745,
-        Hangul_SsangKiyeog = 3746,
-        Hangul_KiyeogSios = 3747,
-        Hangul_Nieun = 3748,
-        Hangul_NieunJieuj = 3749,
-        Hangul_NieunHieuh = 3750,
-        Hangul_Dikeud = 3751,
-        Hangul_SsangDikeud = 3752,
-        Hangul_Rieul = 3753,
-        Hangul_RieulKiyeog = 3754,
-        Hangul_RieulMieum = 3755,
-        Hangul_RieulPieub = 3756,
-        Hangul_RieulSios = 3757,
-        Hangul_RieulTieut = 3758,
-        Hangul_RieulPhieuf = 3759,
-        Hangul_RieulHieuh = 3760,
-        Hangul_Mieum = 3761,
-        Hangul_Pieub = 3762,
-        Hangul_SsangPieub = 3763,
-        Hangul_PieubSios = 3764,
-        Hangul_Sios = 3765,
-        Hangul_SsangSios = 3766,
-        Hangul_Ieung = 3767,
-        Hangul_Jieuj = 3768,
-        Hangul_SsangJieuj = 3769,
-        Hangul_Cieuc = 3770,
-        Hangul_Khieuq = 3771,
-        Hangul_Tieut = 3772,
-        Hangul_Phieuf = 3773,
-        Hangul_Hieuh = 3774,
-        Hangul_A = 3775,
-        Hangul_AE = 3776,
-        Hangul_YA = 3777,
-        Hangul_YAE = 3778,
-        Hangul_EO = 3779,
-        Hangul_E = 3780,
-        Hangul_YEO = 3781,
-        Hangul_YE = 3782,
-        Hangul_O = 3783,
-        Hangul_WA = 3784,
-        Hangul_WAE = 3785,
-        Hangul_OE = 3786,
-        Hangul_YO = 3787,
-        Hangul_U = 3788,
-        Hangul_WEO = 3789,
-        Hangul_WE = 3790,
-        Hangul_WI = 3791,
-        Hangul_YU = 3792,
-        Hangul_EU = 3793,
-        Hangul_YI = 3794,
-        Hangul_I = 3795,
-        Hangul_J_Kiyeog = 3796,
-        Hangul_J_SsangKiyeog = 3797,
-        Hangul_J_KiyeogSios = 3798,
-        Hangul_J_Nieun = 3799,
-        Hangul_J_NieunJieuj = 3800,
-        Hangul_J_NieunHieuh = 3801,
-        Hangul_J_Dikeud = 3802,
-        Hangul_J_Rieul = 3803,
-        Hangul_J_RieulKiyeog = 3804,
-        Hangul_J_RieulMieum = 3805,
-        Hangul_J_RieulPieub = 3806,
-        Hangul_J_RieulSios = 3807,
-        Hangul_J_RieulTieut = 3808,
-        Hangul_J_RieulPhieuf = 3809,
-        Hangul_J_RieulHieuh = 3810,
-        Hangul_J_Mieum = 3811,
-        Hangul_J_Pieub = 3812,
-        Hangul_J_PieubSios = 3813,
-        Hangul_J_Sios = 3814,
-        Hangul_J_SsangSios = 3815,
-        Hangul_J_Ieung = 3816,
-        Hangul_J_Jieuj = 3817,
-        Hangul_J_Cieuc = 3818,
-        Hangul_J_Khieuq = 3819,
-        Hangul_J_Tieut = 3820,
-        Hangul_J_Phieuf = 3821,
-        Hangul_J_Hieuh = 3822,
-        Hangul_RieulYeorinHieuh = 3823,
-        Hangul_SunkyeongeumMieum = 3824,
-        Hangul_SunkyeongeumPieub = 3825,
-        Hangul_PanSios = 3826,
-        Hangul_KkogjiDalrinIeung = 3827,
-        Hangul_SunkyeongeumPhieuf = 3828,
-        Hangul_YeorinHieuh = 3829,
-        Hangul_AraeA = 3830,
-        Hangul_AraeAE = 3831,
-        Hangul_J_PanSios = 3832,
-        Hangul_J_KkogjiDalrinIeung = 3833,
-        Hangul_J_YeorinHieuh = 3834,
-        Korean_Won = 3839,
-        OE = 5052,
-        oe = 5053,
-        Ydiaeresis = 5054,
-        EcuSign = 8352,
-        ColonSign = 8353,
-        CruzeiroSign = 8354,
-        FFrancSign = 8355,
-        LiraSign = 8356,
-        MillSign = 8357,
-        NairaSign = 8358,
-        PesetaSign = 8359,
-        RupeeSign = 8360,
-        WonSign = 8361,
-        NewSheqelSign = 8362,
-        DongSign = 8363,
-        EuroSign = 8364,
-        Key_3270_Duplicate = 64769,
-        Key_3270_FieldMark = 64770,
-        Key_3270_Right2 = 64771,
-        Key_3270_Left2 = 64772,
-        Key_3270_BackTab = 64773,
-        Key_3270_EraseEOF = 64774,
-        Key_3270_EraseInput = 64775,
-        Key_3270_Reset = 64776,
-        Key_3270_Quit = 64777,
-        Key_3270_PA1 = 64778,
-        Key_3270_PA2 = 64779,
-        Key_3270_PA3 = 64780,
-        Key_3270_Test = 64781,
-        Key_3270_Attn = 64782,
-        Key_3270_CursorBlink = 64783,
-        Key_3270_AltCursor = 64784,
-        Key_3270_KeyClick = 64785,
-        Key_3270_Jump = 64786,
-        Key_3270_Ident = 64787,
-        Key_3270_Rule = 64788,
-        Key_3270_Copy = 64789,
-        Key_3270_Play = 64790,
-        Key_3270_Setup = 64791,
-        Key_3270_Record = 64792,
-        Key_3270_ChangeScreen = 64793,
-        Key_3270_DeleteWord = 64794,
-        Key_3270_ExSelect = 64795,
-        Key_3270_CursorSelect = 64796,
-        Key_3270_PrintScreen = 64797,
-        Key_3270_Enter = 64798,
-        ISO_Lock = 65025,
-        ISO_Level2_Latch = 65026,
-        ISO_Level3_Shift = 65027,
-        ISO_Level3_Latch = 65028,
-        ISO_Level3_Lock = 65029,
-        ISO_Group_Latch = 65030,
-        ISO_Group_Lock = 65031,
-        ISO_Next_Group = 65032,
-        ISO_Next_Group_Lock = 65033,
-        ISO_Prev_Group = 65034,
-        ISO_Prev_Group_Lock = 65035,
-        ISO_First_Group = 65036,
-        ISO_First_Group_Lock = 65037,
-        ISO_Last_Group = 65038,
-        ISO_Last_Group_Lock = 65039,
-        ISO_Left_Tab = 65056,
-        ISO_Move_Line_Up = 65057,
-        ISO_Move_Line_Down = 65058,
-        ISO_Partial_Line_Up = 65059,
-        ISO_Partial_Line_Down = 65060,
-        ISO_Partial_Space_Left = 65061,
-        ISO_Partial_Space_Right = 65062,
-        ISO_Set_Margin_Left = 65063,
-        ISO_Set_Margin_Right = 65064,
-        ISO_Release_Margin_Left = 65065,
-        ISO_Release_Margin_Right = 65066,
-        ISO_Release_Both_Margins = 65067,
-        ISO_Fast_Cursor_Left = 65068,
-        ISO_Fast_Cursor_Right = 65069,
-        ISO_Fast_Cursor_Up = 65070,
-        ISO_Fast_Cursor_Down = 65071,
-        ISO_Continuous_Underline = 65072,
-        ISO_Discontinuous_Underline = 65073,
-        ISO_Emphasize = 65074,
-        ISO_Center_Object = 65075,
-        ISO_Enter = 65076,
-        dead_grave = 65104,
-        dead_acute = 65105,
-        dead_circumflex = 65106,
-        dead_tilde = 65107,
-        dead_macron = 65108,
-        dead_breve = 65109,
-        dead_abovedot = 65110,
-        dead_diaeresis = 65111,
-        dead_abovering = 65112,
-        dead_doubleacute = 65113,
-        dead_caron = 65114,
-        dead_cedilla = 65115,
-        dead_ogonek = 65116,
-        dead_iota = 65117,
-        dead_voiced_sound = 65118,
-        dead_semivoiced_sound = 65119,
-        dead_belowdot = 65120,
-        AccessX_Enable = 65136,
-        AccessX_Feedback_Enable = 65137,
-        RepeatKeys_Enable = 65138,
-        SlowKeys_Enable = 65139,
-        BounceKeys_Enable = 65140,
-        StickyKeys_Enable = 65141,
-        MouseKeys_Enable = 65142,
-        MouseKeys_Accel_Enable = 65143,
-        Overlay1_Enable = 65144,
-        Overlay2_Enable = 65145,
-        AudibleBell_Enable = 65146,
-        First_Virtual_Screen = 65232,
-        Prev_Virtual_Screen = 65233,
-        Next_Virtual_Screen = 65234,
-        Last_Virtual_Screen = 65236,
-        Terminate_Server = 65237,
-        Pointer_Left = 65248,
-        Pointer_Right = 65249,
-        Pointer_Up = 65250,
-        Pointer_Down = 65251,
-        Pointer_UpLeft = 65252,
-        Pointer_UpRight = 65253,
-        Pointer_DownLeft = 65254,
-        Pointer_DownRight = 65255,
-        Pointer_Button_Dflt = 65256,
-        Pointer_Button1 = 65257,
-        Pointer_Button2 = 65258,
-        Pointer_Button3 = 65259,
-        Pointer_Button4 = 65260,
-        Pointer_Button5 = 65261,
-        Pointer_DblClick_Dflt = 65262,
-        Pointer_DblClick1 = 65263,
-        Pointer_DblClick2 = 65264,
-        Pointer_DblClick3 = 65265,
-        Pointer_DblClick4 = 65266,
-        Pointer_DblClick5 = 65267,
-        Pointer_Drag_Dflt = 65268,
-        Pointer_Drag1 = 65269,
-        Pointer_Drag2 = 65270,
-        Pointer_Drag3 = 65271,
-        Pointer_Drag4 = 65272,
-        Pointer_EnableKeys = 65273,
-        Pointer_Accelerate = 65274,
-        Pointer_DfltBtnNext = 65275,
-        Pointer_DfltBtnPrev = 65276,
-        Pointer_Drag5 = 65277,
-        BackSpace = 65288,
-        Tab = 65289,
-        Linefeed = 65290,
-        Clear = 65291,
-        Return = 65293,
-        Pause = 65299,
-        Scroll_Lock = 65300,
-        Sys_Req = 65301,
-        Escape = 65307,
-        Multi_key = 65312,
-        Kanji = 65313,
-        Muhenkan = 65314,
-        Henkan = 65315,
-        Henkan_Mode = 65315,
-        Romaji = 65316,
-        Hiragana = 65317,
-        Katakana = 65318,
-        Hiragana_Katakana = 65319,
-        Zenkaku = 65320,
-        Hankaku = 65321,
-        Zenkaku_Hankaku = 65322,
-        Touroku = 65323,
-        Massyo = 65324,
-        Kana_Lock = 65325,
-        Kana_Shift = 65326,
-        Eisu_Shift = 65327,
-        Eisu_toggle = 65328,
-        Hangul = 65329,
-        Hangul_Start = 65330,
-        Hangul_End = 65331,
-        Hangul_Hanja = 65332,
-        Hangul_Jamo = 65333,
-        Hangul_Romaja = 65334,
-        Codeinput = 65335,
-        Hangul_Codeinput = 65335,
-        Kanji_Bangou = 65335,
-        Hangul_Jeonja = 65336,
-        Hangul_Banja = 65337,
-        Hangul_PreHanja = 65338,
-        Hangul_PostHanja = 65339,
-        Hangul_SingleCandidate = 65340,
-        SingleCandidate = 65340,
-        Hangul_MultipleCandidate = 65341,
-        MultipleCandidate = 65341,
-        Zen_Koho = 65341,
-        Hangul_PreviousCandidate = 65342,
-        Mae_Koho = 65342,
-        PreviousCandidate = 65342,
-        Hangul_Special = 65343,
-        Home = 65360,
-        Left = 65361,
-        Up = 65362,
-        Right = 65363,
-        Down = 65364,
-        Page_Up = 65365,
-        Prior = 65365,
-        Next = 65366,
-        Page_Down = 65366,
-        End = 65367,
-        Begin = 65368,
-        Select = 65376,
-        Print = 65377,
-        Execute = 65378,
-        Insert = 65379,
-        Undo = 65381,
-        Redo = 65382,
-        Menu = 65383,
-        Find = 65384,
-        Cancel = 65385,
-        Help = 65386,
-        Break = 65387,
-        Arabic_switch = 65406,
-        Greek_switch = 65406,
-        Hangul_switch = 65406,
-        Hebrew_switch = 65406,
-        ISO_Group_Shift = 65406,
-        Mode_switch = 65406,
-        kana_switch = 65406,
-        script_switch = 65406,
-        Num_Lock = 65407,
-        KP_Space = 65408,
-        KP_Tab = 65417,
-        KP_Enter = 65421,
-        KP_F1 = 65425,
-        KP_F2 = 65426,
-        KP_F3 = 65427,
-        KP_F4 = 65428,
-        KP_Home = 65429,
-        KP_Left = 65430,
-        KP_Up = 65431,
-        KP_Right = 65432,
-        KP_Down = 65433,
-        KP_Page_Up = 65434,
-        KP_Prior = 65434,
-        KP_Next = 65435,
-        KP_Page_Down = 65435,
-        KP_End = 65436,
-        KP_Begin = 65437,
-        KP_Insert = 65438,
-        KP_Delete = 65439,
-        KP_Multiply = 65450,
-        KP_Add = 65451,
-        KP_Separator = 65452,
-        KP_Subtract = 65453,
-        KP_Decimal = 65454,
-        KP_Divide = 65455,
-        KP_0 = 65456,
-        KP_1 = 65457,
-        KP_2 = 65458,
-        KP_3 = 65459,
-        KP_4 = 65460,
-        KP_5 = 65461,
-        KP_6 = 65462,
-        KP_7 = 65463,
-        KP_8 = 65464,
-        KP_9 = 65465,
-        KP_Equal = 65469,
-        F1 = 65470,
-        F2 = 65471,
-        F3 = 65472,
-        F4 = 65473,
-        F5 = 65474,
-        F6 = 65475,
-        F7 = 65476,
-        F8 = 65477,
-        F9 = 65478,
-        F10 = 65479,
-        F11 = 65480,
-        L1 = 65480,
-        F12 = 65481,
-        L2 = 65481,
-        F13 = 65482,
-        L3 = 65482,
-        F14 = 65483,
-        L4 = 65483,
-        F15 = 65484,
-        L5 = 65484,
-        F16 = 65485,
-        L6 = 65485,
-        F17 = 65486,
-        L7 = 65486,
-        F18 = 65487,
-        L8 = 65487,
-        F19 = 65488,
-        L9 = 65488,
-        F20 = 65489,
-        L10 = 65489,
-        F21 = 65490,
-        R1 = 65490,
-        F22 = 65491,
-        R2 = 65491,
-        F23 = 65492,
-        R3 = 65492,
-        F24 = 65493,
-        R4 = 65493,
-        F25 = 65494,
-        R5 = 65494,
-        F26 = 65495,
-        R6 = 65495,
-        F27 = 65496,
-        R7 = 65496,
-        F28 = 65497,
-        R8 = 65497,
-        F29 = 65498,
-        R9 = 65498,
-        F30 = 65499,
-        R10 = 65499,
-        F31 = 65500,
-        R11 = 65500,
-        F32 = 65501,
-        R12 = 65501,
-        F33 = 65502,
-        R13 = 65502,
-        F34 = 65503,
-        R14 = 65503,
-        F35 = 65504,
-        R15 = 65504,
-        Shift_L = 65505,
-        Shift_R = 65506,
-        Control_L = 65507,
-        Control_R = 65508,
-        Caps_Lock = 65509,
-        Shift_Lock = 65510,
-        Meta_L = 65511,
-        Meta_R = 65512,
-        Alt_L = 65513,
-        Alt_R = 65514,
-        Super_L = 65515,
-        Super_R = 65516,
-        Hyper_L = 65517,
-        Hyper_R = 65518,
-        Delete = 65535,
-        VoidSymbol = 16777215,
-    }
-}

+ 0 - 115
src/Gtk/Avalonia.Gtk3/Gtk3ForeignX11SystemDialog.cs

@@ -1,115 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Avalonia.Controls;
-using Avalonia.Controls.Platform;
-using Avalonia.Gtk3.Interop;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
-
-namespace Avalonia.Gtk3
-{
-    public class Gtk3ForeignX11SystemDialog : ISystemDialogImpl
-    {
-        private Task<bool> _initialized;
-        private SystemDialogBase _inner = new SystemDialogBase();
-
-
-        public async Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
-        {
-            await EnsureInitialized();
-            var xid = parent.Handle.Handle;
-            return await await RunOnGtkThread(
-                () => _inner.ShowFileDialogAsync(dialog, GtkWindow.Null, chooser => UpdateParent(chooser, xid)));
-        }
-
-        public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
-        {
-            await EnsureInitialized();
-            var xid = parent.Handle.Handle;
-            return await await RunOnGtkThread(
-                () => _inner.ShowFolderDialogAsync(dialog, GtkWindow.Null, chooser => UpdateParent(chooser, xid)));
-        }
-
-        void UpdateParent(GtkFileChooser chooser, IntPtr xid)
-        {
-            Native.GtkWidgetRealize(chooser);
-            var window = Native.GtkWidgetGetWindow(chooser);
-            var parent = Native.GdkWindowForeignNewForDisplay(GdkDisplay, xid);
-            if (window != IntPtr.Zero && parent != IntPtr.Zero)
-                Native.GdkWindowSetTransientFor(window, parent);
-        }
-        
-        async Task EnsureInitialized()
-        {
-            if (_initialized == null)
-            {
-                var tcs = new TaskCompletionSource<bool>();
-                _initialized = tcs.Task;
-                new Thread(() => GtkThread(tcs))
-                {
-                    IsBackground = true
-                }.Start();
-            }
-
-            if (!(await _initialized))
-                throw new Exception("Unable to initialize GTK on separate thread");
-
-        }
-        
-        Task<T> RunOnGtkThread<T>(Func<T> action)
-        {
-            var tcs = new TaskCompletionSource<T>();
-            GlibTimeout.Add(0, 0, () =>
-            {
-
-                try
-                {
-                    tcs.SetResult(action());
-                }
-                catch (Exception e)
-                {
-                    tcs.TrySetException(e);
-                }
-
-                return false;
-            });
-            return tcs.Task;
-        }
-        
-
-        void GtkThread(TaskCompletionSource<bool> tcs)
-        {
-            try
-            {
-                X11.XInitThreads();
-            }catch{}
-            Resolver.Resolve();
-            if (Native.GdkWindowForeignNewForDisplay == null)
-                throw new Exception("gdk_x11_window_foreign_new_for_display is not found in your libgdk-3.so");
-            using (var backends = new Utf8Buffer("x11"))
-                Native.GdkSetAllowedBackends?.Invoke(backends);
-            if (!Native.GtkInitCheck(0, IntPtr.Zero))
-            {
-                tcs.SetResult(false);
-                return;
-            }
-
-            using (var utf = new Utf8Buffer($"avalonia.app.a{Guid.NewGuid().ToString("N")}"))
-                App = Native.GtkApplicationNew(utf, 0);
-            if (App == IntPtr.Zero)
-            {
-                tcs.SetResult(false);
-                return;
-            }
-            GdkDisplay = Native.GdkGetDefaultDisplay();
-            tcs.SetResult(true);
-            while (true)
-                Native.GtkMainIteration();
-        }
-
-        private IntPtr GdkDisplay { get; set; }
-        private IntPtr App { get; set; }
-    }
-}

+ 0 - 168
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@@ -1,168 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Threading;
-using Avalonia.Controls;
-using Avalonia.Controls.Platform;
-using Avalonia.Gtk3;
-using Avalonia.Gtk3.Interop;
-using Avalonia.Input;
-using Avalonia.Input.Platform;
-using Avalonia.OpenGL;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
-using Avalonia.Rendering;
-using Avalonia.Threading;
-
-namespace Avalonia.Gtk3
-{
-    public class Gtk3Platform : IWindowingPlatform, IPlatformSettings, IPlatformThreadingInterface
-    {
-        internal static readonly Gtk3Platform Instance = new Gtk3Platform();
-        internal static readonly MouseDevice Mouse = new MouseDevice();
-        internal static readonly KeyboardDevice Keyboard = new KeyboardDevice();
-        internal static IntPtr App { get; set; }
-        internal static string DisplayClassName;
-        public static bool UseDeferredRendering = true;
-        private static bool s_gtkInitialized;
-
-        static bool EnvOption(string option, bool def, bool? specified)
-        {
-            bool? Parse(string env)
-            {
-                var v = Environment.GetEnvironmentVariable("AVALONIA_GTK3_" + env);
-                if (v == null)
-                    return null;
-                if (v.ToLowerInvariant() == "false" || v == "0")
-                    return false;
-                return true;
-            }
-
-            var overridden = Parse(option + "_OVERRIDE");
-            if (overridden.HasValue)
-                return overridden.Value;
-            if (specified.HasValue)
-                return specified.Value;
-            var envValue = Parse(option);
-            return envValue ?? def;
-        }
-        
-        public static void Initialize(Gtk3PlatformOptions options)
-        {
-            Resolver.Custom = options.CustomResolver;
-            UseDeferredRendering = EnvOption("USE_DEFERRED_RENDERING", true, options.UseDeferredRendering);
-            var useGpu = EnvOption("USE_GPU", true, options.UseGpuAcceleration);
-            if (!s_gtkInitialized)
-            {
-                try
-                {
-                    X11.XInitThreads();
-                }catch{}
-                Resolver.Resolve();
-                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
-                    using (var backends = new Utf8Buffer("x11"))
-                        Native.GdkSetAllowedBackends?.Invoke(backends);
-                Native.GtkInit(0, IntPtr.Zero);
-                var disp = Native.GdkGetDefaultDisplay();
-                DisplayClassName =
-                    Utf8Buffer.StringFromPtr(Native.GTypeName(Marshal.ReadIntPtr(Marshal.ReadIntPtr(disp))));
-
-                using (var utf = new Utf8Buffer($"avalonia.app.a{Guid.NewGuid().ToString("N")}"))
-                    App = Native.GtkApplicationNew(utf, 0);
-                //Mark current thread as UI thread
-                s_tlsMarker = true;
-                s_gtkInitialized = true;
-            }
-            AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatform>().ToConstant(Instance)
-                .Bind<IClipboard>().ToSingleton<ClipboardImpl>()
-                .Bind<IStandardCursorFactory>().ToConstant(new CursorFactory())
-                .Bind<IKeyboardDevice>().ToConstant(Keyboard)
-                .Bind<IPlatformSettings>().ToConstant(Instance)
-                .Bind<IPlatformThreadingInterface>().ToConstant(Instance)
-                .Bind<ISystemDialogImpl>().ToSingleton<SystemDialog>()
-                .Bind<IRenderLoop>().ToConstant(new RenderLoop())
-                .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
-                .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
-                .Bind<IPlatformIconLoader>().ToConstant(new PlatformIconLoader());
-            if (useGpu)
-                EglGlPlatformFeature.TryInitialize();
-        }
-
-        public IWindowImpl CreateWindow() => new WindowImpl();
-
-        public IEmbeddableWindowImpl CreateEmbeddableWindow()
-        {
-            throw new NotImplementedException();
-        }
-
-        public IPopupImpl CreatePopup() => new PopupImpl();
-
-        public Size DoubleClickSize => new Size(4, 4);
-
-        public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(100); //STUB
-        public double RenderScalingFactor { get; } = 1;
-        public double LayoutScalingFactor { get; } = 1;
-
-        public void RunLoop(CancellationToken cancellationToken)
-        {
-            while (!cancellationToken.IsCancellationRequested)
-                Native.GtkMainIteration();
-        }
-
-        public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
-        {
-            var msec = interval.TotalMilliseconds;
-            var imsec = (uint) msec;
-            if (imsec == 0)
-                imsec = 1;
-            return GlibTimeout.StartTimer(GlibPriority.FromDispatcherPriority(priority), imsec, tick);
-        }
-
-        private bool[] _signaled = new bool[(int) DispatcherPriority.MaxValue + 1];
-        object _lock = new object();
-        public void Signal(DispatcherPriority prio)
-        {
-            var idx = (int) prio;
-            lock(_lock)
-                if (!_signaled[idx])
-                {
-                    _signaled[idx] = true;
-                    GlibTimeout.Add(GlibPriority.FromDispatcherPriority(prio), 0, () =>
-                    {
-                        lock (_lock)
-                        {
-                            _signaled[idx] = false;
-                        }
-                        Signaled?.Invoke(prio);
-                        return false;
-                    });
-                }
-        }
-        public event Action<DispatcherPriority?> Signaled;
-
-
-        [ThreadStatic]
-        private static bool s_tlsMarker;
-
-        public bool CurrentThreadIsLoopThread => s_tlsMarker;
-    }
-
-    public class Gtk3PlatformOptions
-    {
-        public bool? UseDeferredRendering { get; set; }
-        public bool? UseGpuAcceleration { get; set; }
-        public ICustomGtk3NativeLibraryResolver CustomResolver { get; set; }
-    }
-}
-
-namespace Avalonia
-{
-    public static class Gtk3AppBuilderExtensions
-    {
-        public static T UseGtk3<T>(this AppBuilderBase<T> builder, Gtk3PlatformOptions options = null) 
-            where T : AppBuilderBase<T>, new()
-        {
-            return builder.UseWindowingSubsystem(() => Gtk3Platform.Initialize(options ?? new Gtk3PlatformOptions()),
-                "GTK3");
-        }
-    }
-}

+ 0 - 24
src/Gtk/Avalonia.Gtk3/GtkScreen.cs

@@ -1,24 +0,0 @@
-using Avalonia.Platform;
-
-namespace Avalonia.Gtk3
-{
-    public class GtkScreen : Screen
-    {
-        private readonly int _screenId;
-        
-        public GtkScreen(PixelRect bounds, PixelRect workingArea, bool primary, int screenId) : base(bounds, workingArea, primary)
-        {
-            this._screenId = screenId;
-        }
-
-        public override int GetHashCode()
-        {
-            return _screenId;
-        }
-
-        public override bool Equals(object obj)
-        {
-            return (obj is GtkScreen screen) ? this._screenId == screen._screenId : base.Equals(obj);
-        }
-    }
-}

+ 0 - 9
src/Gtk/Avalonia.Gtk3/IDeferredRenderOperation.cs

@@ -1,9 +0,0 @@
-using System;
-
-namespace Avalonia.Gtk3
-{
-    public interface IDeferredRenderOperation : IDisposable
-    {
-        void RenderNow(IntPtr? ctx);
-    }
-}

+ 0 - 144
src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs

@@ -1,144 +0,0 @@
-using System;
-using Avalonia.Gtk3.Interop;
-using Avalonia.Platform;
-using Avalonia.Threading;
-
-
-namespace Avalonia.Gtk3
-{
-    class ImageSurfaceFramebuffer : ILockedFramebuffer
-    {
-        private readonly WindowBaseImpl _impl;
-        private readonly GtkWidget _widget;
-        private ManagedCairoSurface _surface;
-        private int _factor;
-        private object _lock = new object();
-        public ImageSurfaceFramebuffer(WindowBaseImpl impl, int width, int height, int factor)
-        {
-            _impl = impl;
-            _widget = impl.GtkWidget;
-            _factor = factor;
-            width *= _factor;
-            height *= _factor;
-            _surface = new ManagedCairoSurface(width, height);
-            
-            Size = new PixelSize(width, height);
-            Address = _surface.Buffer;
-            RowBytes = _surface.Stride;
-            Native.CairoSurfaceFlush(_surface.Surface);
-        }
-
-        static void Draw(IntPtr context, CairoSurface surface, double factor)
-        {
-            
-            Native.CairoSurfaceMarkDirty(surface);
-            Native.CairoScale(context, 1d / factor, 1d / factor);
-            Native.CairoSetSourceSurface(context, surface, 0, 0);
-            Native.CairoPaint(context);
-
-        }
-        /*
-        static Stopwatch St =Stopwatch.StartNew();
-        private static int _frames;
-        private static int _fps;*/
-        static void DrawToWidget(GtkWidget widget, CairoSurface surface, int width, int height, double factor)
-        {
-            if(surface == null || widget.IsClosed)
-                return;
-            var window = Native.GtkWidgetGetWindow(widget);
-            if(window == IntPtr.Zero)
-                return;
-            var rc = new GdkRectangle {Width = width, Height = height};
-            Native.GdkWindowBeginPaintRect(window, ref rc);
-            var context = Native.GdkCairoCreate(window);
-            Draw(context, surface, factor);
-            /*
-            _frames++;
-            var el = St.Elapsed;
-            if (el.TotalSeconds > 1)
-            {
-                _fps = (int) (_frames / el.TotalSeconds);
-                _frames = 0;
-                St = Stopwatch.StartNew();
-            }
-            
-            Native.CairoSetSourceRgba(context, 1, 0, 0, 1);
-            Native.CairoMoveTo(context, 20, 20);
-            Native.CairoSetFontSize(context, 30);
-            using (var txt = new Utf8Buffer("FPS: " + _fps))
-                Native.CairoShowText(context, txt);
-            */
-            
-            Native.CairoDestroy(context);
-            Native.GdkWindowEndPaint(window);
-        }
-        
-        class RenderOp : IDeferredRenderOperation
-        {
-            private readonly GtkWidget _widget;
-            private ManagedCairoSurface _surface;
-            private readonly double _factor;
-            private readonly int _width;
-            private readonly int _height;
-
-            public RenderOp(GtkWidget widget, ManagedCairoSurface surface, double factor, int width, int height)
-            {
-                _widget = widget;
-                _surface = surface ?? throw new ArgumentNullException(nameof(surface));
-                _factor = factor;
-                _width = width;
-                _height = height;
-            }
-
-            public void Dispose()
-            {
-                _surface?.Dispose();
-                _surface = null;
-            }
-
-            public void RenderNow(IntPtr? ctx)
-            {
-                if(ctx.HasValue)
-                    Draw(ctx.Value, _surface.Surface, _factor);
-                else
-                    DrawToWidget(_widget, _surface.Surface, _width, _height, _factor);
-            }
-        }
-        
-        public void Dispose()
-        {
-            lock (_lock)
-            {
-                if (Dispatcher.UIThread.CheckAccess())
-                {
-                    if (_impl.CurrentCairoContext != IntPtr.Zero)
-                        Draw(_impl.CurrentCairoContext, _surface.Surface, _factor);
-                    else
-                        DrawToWidget(_widget, _surface.Surface, Size.Width, Size.Height, _factor);
-                    _surface.Dispose();
-                }
-                else
-                    _impl.SetNextRenderOperation(new RenderOp(_widget, _surface, _factor, Size.Width, Size.Height));
-                _surface = null;
-            }
-        }
-
-        public IntPtr Address { get; }
-        public PixelSize Size { get; }
-        public int RowBytes { get; }
-
-        
-        public Vector Dpi
-        {
-            get
-            {
-                return new Vector(96, 96) * _factor;
-            }
-        }
-
-        public PixelFormat Format => PixelFormat.Bgra8888;
-    }
-}
-
-
-

+ 0 - 20
src/Gtk/Avalonia.Gtk3/Interop/CairoSurface.cs

@@ -1,20 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Avalonia.Gtk3.Interop
-{
-    class CairoSurface : SafeHandle
-    {
-        public CairoSurface() : base(IntPtr.Zero, true)
-        {
-        }
-
-        protected override bool ReleaseHandle()
-        {
-            Native.CairoSurfaceDestroy(handle);
-            return true;
-        }
-
-        public override bool IsInvalid => handle == IntPtr.Zero;
-    }
-}

+ 0 - 30
src/Gtk/Avalonia.Gtk3/Interop/GException.cs

@@ -1,30 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using Avalonia.Platform.Interop;
-
-namespace Avalonia.Gtk3.Interop
-{
-    public class GException : Exception
-    {
-        [StructLayout(LayoutKind.Sequential)]
-        struct GError
-        {
-            UInt32 domain;
-            int code;
-            public IntPtr message;
-        };
-
-        static unsafe string GetError(IntPtr error)
-        {
-            if (error == IntPtr.Zero)
-                return "Unknown error";
-            return Utf8Buffer.StringFromPtr(((GError*) error)->message);
-        }
-
-        public GException(IntPtr error) : base(GetError(error))
-        {
-            
-        }
-
-    }
-}

+ 0 - 87
src/Gtk/Avalonia.Gtk3/Interop/GObject.cs

@@ -1,87 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace Avalonia.Gtk3.Interop
-{
-    class GObject : SafeHandle
-    {
-        public GObject() : base(IntPtr.Zero, true)
-        {
-        }
-
-        public GObject(IntPtr handle, bool owned = true) : base(IntPtr.Zero, owned)
-        {
-            this.handle = handle;
-        }
-
-        protected override bool ReleaseHandle()
-        {
-            if (handle != IntPtr.Zero)
-            {
-                Debug.Assert(Native.GTypeCheckInstanceIsFundamentallyA(handle, new IntPtr(Native.G_TYPE_OBJECT)),
-                    "Handle is not a GObject");
-                Native.GObjectUnref(handle);
-            }
-
-            handle = IntPtr.Zero;
-            return true;
-        }
-
-        public override bool IsInvalid => handle == IntPtr.Zero;
-    }
-
-    class GInputStream : GObject
-    {
-        
-    }
-
-    class GtkWidget : GObject
-    {
-        
-    }
-
-    class GtkWindow : GtkWidget
-    {
-        public static GtkWindow Null { get; } = new GtkWindow();
-    }
-
-    class GtkImContext : GObject
-    {
-    }
-
-    class GdkScreen : GObject
-    {
-        public GdkScreen() : base(IntPtr.Zero, false)
-        {
-        }
-
-        public GdkScreen(IntPtr handle, bool owned = true) : base(handle, owned)
-        {
-            this.handle = handle;
-        }
-    }
-
-    class UnownedGdkScreen : GdkScreen
-    {
-        public UnownedGdkScreen() : base(IntPtr.Zero, false)
-        {
-        }
-
-        public UnownedGdkScreen(IntPtr handle, bool owned = true) : base(IntPtr.Zero, false)
-        {
-            this.handle = handle;
-        }
-    }
-
-    class GtkDialog : GtkWindow
-    {
-        
-    }
-
-    class GtkFileChooser : GtkDialog
-    {
-        
-    }
-}
-

+ 0 - 46
src/Gtk/Avalonia.Gtk3/Interop/GlibPriority.cs

@@ -1,46 +0,0 @@
-using System;
-using Avalonia.Threading;
-
-namespace Avalonia.Gtk3.Interop
-{
-    static class GlibPriority
-    {
-        public static int High = -100;
-        public static int Default = 0;
-        public static int HighIdle = 100;
-        public static int GtkResize = HighIdle + 10;
-        public static int GtkPaint = HighIdle + 20;
-        public static int DefaultIdle = 200;
-        public static int Low = 300;
-        public static int GdkEvents = Default;
-        public static int GdkRedraw = HighIdle + 20;
-
-        public static int FromDispatcherPriority(DispatcherPriority prio)
-        {
-            if (prio == DispatcherPriority.Send)
-                return High;
-            if (prio == DispatcherPriority.Normal)
-                return Default;
-            if (prio == DispatcherPriority.DataBind)
-                return Default + 1;
-            if (prio == DispatcherPriority.Layout)
-                return Default + 2;
-            if (prio == DispatcherPriority.Render)
-                return Default + 3;
-            if (prio == DispatcherPriority.Loaded)
-                return GtkPaint + 20;
-            if (prio == DispatcherPriority.Input)
-                return GtkPaint + 21;
-            if (prio == DispatcherPriority.Background)
-                return DefaultIdle + 1;
-            if (prio == DispatcherPriority.ContextIdle)
-                return DefaultIdle + 2;
-            if (prio == DispatcherPriority.ApplicationIdle)
-                return DefaultIdle + 3;
-            if (prio == DispatcherPriority.SystemIdle)
-                return DefaultIdle + 4;
-            throw new ArgumentException("Unknown priority");
-
-        }
-    }
-}

+ 0 - 58
src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs

@@ -1,58 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Avalonia.Gtk3.Interop
-{
-    static class GlibTimeout
-    {
-        static bool Handler(IntPtr data)
-        {
-            var handle = GCHandle.FromIntPtr(data);
-            var cb = (Func<bool>) handle.Target;
-            if (!cb())
-            {
-                handle.Free();
-                return false;
-            }
-            return true;
-        }
-        
-        private static readonly Native.D.timeout_callback PinnedHandler;
-        static GlibTimeout()
-        {
-            PinnedHandler = Handler;
-        }
-
-
-        public static void Add(int priority, uint interval, Func<bool> callback)
-        {
-            var handle = GCHandle.Alloc(callback);
-            Native.GTimeoutAddFull(priority, interval, PinnedHandler, GCHandle.ToIntPtr(handle), IntPtr.Zero);
-        }
-
-        class Timer : IDisposable
-        {
-            public bool Stopped;
-            public void Dispose()
-            {
-
-                Stopped = true;
-            }
-        }
-
-        public static IDisposable StartTimer(int priority, uint interval, Action tick)
-        {
-            var timer = new Timer ();
-            GlibTimeout.Add(priority, interval,
-                () =>
-                {
-                    if (timer.Stopped)
-                        return false;
-                    tick();
-                    return !timer.Stopped;
-                });
-
-            return timer;
-        }
-    }
-}

+ 0 - 12
src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs

@@ -1,12 +0,0 @@
-using Avalonia.Gtk3.Interop;
-
-namespace Avalonia.Gtk3
-{
-    public interface ICustomGtk3NativeLibraryResolver
-    {
-        string GetName(GtkDll dll);
-        string BasePath { get; }
-        bool TrySystemFirst { get; }
-        string Lookup(GtkDll dll);
-    }
-}

+ 0 - 37
src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs

@@ -1,37 +0,0 @@
-using System;
-using Avalonia.Platform;
-
-namespace Avalonia.Gtk3.Interop
-{
-    class ManagedCairoSurface : IDisposable
-    {
-        public IntPtr Buffer { get; private set; }
-        public CairoSurface Surface { get; private set; }
-        public int Stride { get; private set; }
-        private int _size;
-        private IRuntimePlatform _plat;
-        private IUnmanagedBlob _blob;
-
-        public ManagedCairoSurface(int width, int height)
-        {
-            _plat = AvaloniaLocator.Current.GetService<IRuntimePlatform>();
-            Stride = width * 4;
-            _size = height * Stride;
-            _blob = _plat.AllocBlob(_size * 2);
-            Buffer = _blob.Address;
-            Surface = Native.CairoImageSurfaceCreateForData(Buffer, 1, width, height, Stride);
-        }
-        
-        public void Dispose()
-        {
-            
-            if (Buffer != IntPtr.Zero)
-            {
-                Surface.Dispose();
-                _blob.Dispose();
-                Buffer = IntPtr.Zero;
-            }
-        }
-
-    }
-}

+ 0 - 790
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@@ -1,790 +0,0 @@
-#pragma warning disable 649
-using System;
-using System.Runtime.InteropServices;
-using Avalonia.Controls;
-using Avalonia.Platform.Interop;
-using gdouble = System.Double;
-using gint = System.Int32;
-using gint16 = System.Int16;
-using gint8 = System.Byte;
-using guint = System.UInt32;
-using guint16 = System.UInt16;
-using guint32 = System.UInt32;
-
-namespace Avalonia.Gtk3.Interop
-{
-    static class Native
-    {
-        public static class D
-        {
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate gint16 gdk_display_get_n_screens(IntPtr display);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate UnownedGdkScreen gdk_display_get_screen(IntPtr display, gint16 num);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate UnownedGdkScreen gdk_display_get_default_screen (IntPtr display);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate gint16 gdk_screen_get_n_monitors(GdkScreen screen);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate gint16 gdk_screen_get_primary_monitor(GdkScreen screen);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_screen_get_monitor_geometry(GdkScreen screen, gint16 num, ref GdkRectangle rect);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_screen_get_monitor_workarea(GdkScreen screen, gint16 num, ref GdkRectangle rect);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate IntPtr gtk_application_new(Utf8Buffer appId, int flags);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_main_iteration();
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate GtkWindow gtk_window_new(GtkWindowType windowType);           
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate IntPtr gtk_init(int argc, IntPtr argv);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate bool gtk_init_check(int argc, IntPtr argv);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk, optional: true)]
-            public delegate IntPtr gdk_set_allowed_backends (Utf8Buffer backends);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_present(GtkWindow gtkWindow);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_widget_hide(GtkWidget gtkWidget);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_widget_show(GtkWidget gtkWidget);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_icon(GtkWindow window, Pixbuf pixbuf);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_modal(GtkWindow window, bool modal);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_transient_for(GtkWindow window, IntPtr parent);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl)] //No manual import
-            public delegate IntPtr gdk_get_native_handle(IntPtr gdkWindow);
-
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate IntPtr gtk_widget_get_window(GtkWidget gtkWidget);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk, optional: true)]
-            public delegate uint gtk_widget_get_scale_factor(GtkWidget gtkWidget);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate IntPtr gtk_widget_get_screen(GtkWidget gtkWidget);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate IntPtr gtk_widget_set_double_buffered(GtkWidget gtkWidget, bool value);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate IntPtr gtk_widget_set_events(GtkWidget gtkWidget, uint flags);
-
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate int gdk_screen_get_height(IntPtr screen);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate int gdk_screen_get_width(IntPtr screen);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate IntPtr gdk_display_get_default();
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate int gdk_window_get_origin(IntPtr gdkWindow, out int x, out int y);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_window_resize(IntPtr gtkWindow, int width, int height);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_window_set_override_redirect(IntPtr gdkWindow, bool value);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_widget_realize(GtkWidget gtkWidget);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_title(GtkWindow gtkWindow, Utf8Buffer title);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_resizable(GtkWindow gtkWindow, bool resizable);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_decorated(GtkWindow gtkWindow, bool decorated);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_skip_taskbar_hint(GtkWindow gtkWindow, bool setting); 
-            
-             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate bool gtk_window_get_skip_taskbar_hint(GtkWindow gtkWindow);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_skip_pager_hint(GtkWindow gtkWindow, bool setting); 
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate bool gtk_window_get_skip_pager_hint(GtkWindow gtkWindow); 
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_get_size(GtkWindow gtkWindow, out int width, out int height);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_resize(GtkWindow gtkWindow, int width, int height);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_widget_set_size_request(GtkWidget widget, int width, int height);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_default_size(GtkWindow gtkWindow, int width, int height);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_get_position(GtkWindow gtkWindow, out int x, out int y);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_move(GtkWindow gtkWindow, int x, int y);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate GtkFileChooser gtk_file_chooser_dialog_new(Utf8Buffer title, GtkWindow parent, GtkFileChooserAction action, IntPtr ignore);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public unsafe delegate GSList* gtk_file_chooser_get_filenames(GtkFileChooser chooser);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_file_chooser_set_select_multiple(GtkFileChooser chooser, bool allow);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_file_chooser_set_filename(GtkFileChooser chooser, Utf8Buffer file);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_dialog_add_button(GtkDialog raw, Utf8Buffer button_text, GtkResponseType response_id);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate CairoSurface cairo_image_surface_create(int format, int width, int height);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate CairoSurface cairo_image_surface_create_for_data(IntPtr data, int format, int width, int height, int stride);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate IntPtr cairo_image_surface_get_data(CairoSurface surface);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate int cairo_image_surface_get_stride(CairoSurface surface);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_surface_mark_dirty(CairoSurface surface);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_surface_write_to_png(CairoSurface surface, Utf8Buffer path);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_surface_flush(CairoSurface surface);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_surface_destroy(IntPtr surface);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_set_source_surface(IntPtr cr, CairoSurface surface, double x, double y);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_set_source_rgba(IntPtr cr, double r, double g, double b, double a);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_scale(IntPtr context, double sx, double sy);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_paint(IntPtr context);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_show_text(IntPtr context, Utf8Buffer text);   
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_set_font_size(IntPtr context, double size);  
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_select_font_face(IntPtr context, Utf8Buffer face, int slant, int weight);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_move_to(IntPtr context, double x, double y);   
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
-            public delegate void cairo_destroy(IntPtr context);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_widget_queue_draw_area(GtkWidget widget, int x, int y, int width, int height);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate uint gtk_widget_add_tick_callback(GtkWidget widget, TickCallback callback, IntPtr userData, IntPtr destroy);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate uint gtk_widget_remove_tick_callback(GtkWidget widget, uint id);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate GtkImContext gtk_im_multicontext_new();
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate IntPtr gtk_im_context_set_client_window(GtkImContext context, IntPtr window);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate bool gtk_im_context_filter_keypress(GtkImContext context, IntPtr ev);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_widget_activate(GtkWidget widget);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate IntPtr gdk_screen_get_root_window(IntPtr screen);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate IntPtr gdk_cursor_new(GdkCursorType type);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate IntPtr gdk_window_get_pointer(IntPtr raw, out int x, out int y, out int mask);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate GdkWindowState gdk_window_get_state(IntPtr window);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_iconify(GtkWindow window);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_deiconify(GtkWindow window);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_maximize(GtkWindow window);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_unmaximize(GtkWindow window);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_close(GtkWindow window);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_keep_above(GtkWindow gtkWindow, bool setting);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_window_set_geometry_hints(GtkWindow window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_window_invalidate_rect(IntPtr window, ref GdkRectangle rect, bool invalidate_children);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_window_begin_move_drag(IntPtr window, gint button, gint root_x, gint root_y, guint32 timestamp);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_window_begin_resize_drag(IntPtr window, WindowEdge edge, gint button, gint root_x, gint root_y, guint32 timestamp);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_window_process_updates(IntPtr window, bool updateChildren);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_window_begin_paint_rect(IntPtr window, ref GdkRectangle rect);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_window_end_paint(IntPtr window);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk, optional: true)]
-            public delegate IntPtr gdk_x11_window_foreign_new_for_display(IntPtr display, IntPtr xid);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_window_set_transient_for(IntPtr window, IntPtr parent);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate void gdk_event_request_motions(IntPtr ev);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate IntPtr gtk_clipboard_get_for_display(IntPtr display, IntPtr atom);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_clipboard_request_text(IntPtr clipboard, GtkClipboardTextReceivedFunc callback, IntPtr user_data);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_clipboard_set_text(IntPtr clipboard, Utf8Buffer text, int len);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate void gtk_clipboard_clear(IntPtr clipboard);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)]
-            public delegate IntPtr gdk_pixbuf_new_from_file(Utf8Buffer filename, out IntPtr error);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate IntPtr gtk_icon_theme_get_default();
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
-            public delegate IntPtr gtk_icon_theme_load_icon(IntPtr icon_theme, Utf8Buffer icon_name, gint size, int flags,out IntPtr error);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate IntPtr gdk_cursor_new_from_pixbuf(IntPtr disp, IntPtr pixbuf, int x, int y);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate IntPtr gdk_window_set_cursor(IntPtr window, IntPtr cursor);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)]
-            public delegate IntPtr gdk_pixbuf_new_from_stream(GInputStream stream, IntPtr cancel, out IntPtr error);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)]
-            public delegate bool gdk_pixbuf_save_to_bufferv(Pixbuf pixbuf, out IntPtr buffer, out IntPtr buffer_size,
-                            Utf8Buffer type, IntPtr option_keys, IntPtr option_values, out IntPtr error);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
-            public delegate IntPtr gdk_cairo_create(IntPtr window);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
-            public delegate void g_object_unref(IntPtr instance);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
-            public delegate void g_object_ref(GObject instance);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
-            public delegate IntPtr g_type_name(IntPtr instance);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
-            public delegate ulong g_signal_connect_object(GObject instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
-            public delegate ulong g_signal_handler_disconnect(GObject instance, ulong connectionId);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)]
-            public delegate ulong g_timeout_add(uint interval, timeout_callback callback, IntPtr data);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)]
-            public delegate ulong g_timeout_add_full(int prio, uint interval, timeout_callback callback, IntPtr data, IntPtr destroy);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)]
-            public delegate ulong g_free(IntPtr data);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
-            public delegate bool g_type_check_instance_is_fundamentally_a(IntPtr instance, IntPtr type);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)]
-            public unsafe delegate void g_slist_free(GSList* data);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gio)]
-            public delegate GInputStream g_memory_input_stream_new_from_data(IntPtr ptr, IntPtr len, IntPtr destroyCallback);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-            public delegate bool signal_widget_draw(IntPtr gtkWidget, IntPtr cairoContext, IntPtr userData);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-            public delegate bool signal_generic(IntPtr gtkWidget, IntPtr userData);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-            public delegate bool signal_dialog_response(IntPtr gtkWidget, GtkResponseType response, IntPtr userData);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-            public delegate bool signal_onevent(IntPtr gtkWidget, IntPtr ev, IntPtr userData);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-            public delegate void monitors_changed(IntPtr screen, IntPtr userData);
-            
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-            public delegate bool signal_commit(IntPtr gtkWidget, IntPtr utf8string, IntPtr userData);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-            public delegate bool timeout_callback(IntPtr data);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-            public delegate void GtkClipboardTextReceivedFunc(IntPtr clipboard, IntPtr utf8string, IntPtr userdata);
-
-            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-            public delegate bool TickCallback(IntPtr widget, IntPtr clock, IntPtr userdata);
-
-
-        }
-
-        public static D.gdk_display_get_n_screens GdkDisplayGetNScreens;
-        public static D.gdk_display_get_screen GdkDisplayGetScreen;
-        public static D.gdk_display_get_default_screen GdkDisplayGetDefaultScreen;
-        public static D.gdk_screen_get_n_monitors GdkScreenGetNMonitors;
-        public static D.gdk_screen_get_primary_monitor GdkScreenGetPrimaryMonitor;
-        public static D.gdk_screen_get_monitor_geometry GdkScreenGetMonitorGeometry;
-        public static D.gdk_screen_get_monitor_workarea GdkScreenGetMonitorWorkarea;
-        public static D.gtk_window_set_decorated GtkWindowSetDecorated;
-        public static D.gtk_window_set_resizable GtkWindowSetResizable;
-        public static D.gtk_window_set_skip_taskbar_hint GtkWindowSetSkipTaskbarHint;
-        public static D.gtk_window_get_skip_taskbar_hint GtkWindowGetSkipTaskbarHint;
-        public static D.gtk_window_set_skip_pager_hint GtkWindowSetSkipPagerHint;
-        public static D.gtk_window_get_skip_pager_hint GtkWindowGetSkipPagerHint;
-        public static D.gtk_window_set_title GtkWindowSetTitle;
-        public static D.gtk_application_new GtkApplicationNew;
-        public static D.gtk_main_iteration GtkMainIteration;
-        public static D.gtk_window_new GtkWindowNew;
-        public static D.gtk_window_set_icon GtkWindowSetIcon;
-        public static D.gtk_window_set_modal GtkWindowSetModal;
-        public static D.gtk_window_set_transient_for GtkWindowSetTransientFor;
-        public static D.gdk_set_allowed_backends GdkSetAllowedBackends;
-        public static D.gtk_init GtkInit;
-        public static D.gtk_init_check GtkInitCheck;
-        public static D.gtk_window_present GtkWindowPresent;
-        public static D.gtk_widget_hide GtkWidgetHide;
-        public static D.gtk_widget_show GtkWidgetShow;
-        public static D.gdk_get_native_handle GetNativeGdkWindowHandle;
-        public static D.gtk_widget_get_window GtkWidgetGetWindow;
-        public static D.gtk_widget_get_scale_factor GtkWidgetGetScaleFactor;
-        public static D.gtk_widget_get_screen GtkWidgetGetScreen;
-        public static D.gtk_widget_realize GtkWidgetRealize;
-        public static D.gtk_window_get_size GtkWindowGetSize;
-        public static D.gtk_window_resize GtkWindowResize;
-        public static D.gdk_window_resize GdkWindowResize;
-        public static D.gdk_window_set_override_redirect GdkWindowSetOverrideRedirect;
-        public static D.gtk_widget_set_size_request GtkWindowSetSizeRequest;
-        public static D.gtk_window_set_default_size GtkWindowSetDefaultSize;
-        public static D.gtk_window_set_geometry_hints GtkWindowSetGeometryHints;
-        public static D.gtk_window_get_position GtkWindowGetPosition;
-        public static D.gtk_window_move GtkWindowMove;
-        public static D.gtk_file_chooser_dialog_new GtkFileChooserDialogNew;
-        public static D.gtk_file_chooser_set_select_multiple GtkFileChooserSetSelectMultiple;
-        public static D.gtk_file_chooser_set_filename GtkFileChooserSetFilename;
-        public static D.gtk_file_chooser_get_filenames GtkFileChooserGetFilenames;
-        public static D.gtk_dialog_add_button GtkDialogAddButton;
-        public static D.g_object_unref GObjectUnref;
-        public static D.g_object_ref GObjectRef;
-        public static D.g_type_name GTypeName;
-        public static D.g_signal_connect_object GSignalConnectObject;
-        public static D.g_signal_handler_disconnect GSignalHandlerDisconnect;
-        public static D.g_timeout_add GTimeoutAdd;
-        public static D.g_timeout_add_full GTimeoutAddFull;
-        public static D.g_free GFree;
-        public static D.g_type_check_instance_is_fundamentally_a GTypeCheckInstanceIsFundamentallyA;
-        public static D.g_slist_free GSlistFree;
-        public static D.g_memory_input_stream_new_from_data GMemoryInputStreamNewFromData;
-        public static D.gtk_widget_set_double_buffered GtkWidgetSetDoubleBuffered;
-        public static D.gtk_widget_set_events GtkWidgetSetEvents;
-        public static D.gdk_window_invalidate_rect GdkWindowInvalidateRect;
-        public static D.gtk_widget_queue_draw_area GtkWidgetQueueDrawArea;
-        public static D.gtk_widget_add_tick_callback GtkWidgetAddTickCallback;
-        public static D.gtk_widget_remove_tick_callback GtkWidgetRemoveTickCallback;
-        public static D.gtk_widget_activate GtkWidgetActivate;
-        public static D.gtk_clipboard_get_for_display GtkClipboardGetForDisplay;
-        public static D.gtk_clipboard_request_text GtkClipboardRequestText;
-        public static D.gtk_clipboard_set_text GtkClipboardSetText;
-        public static D.gtk_clipboard_clear GtkClipboardRequestClear;
-        
-        public static D.gtk_im_multicontext_new GtkImMulticontextNew;
-        public static D.gtk_im_context_filter_keypress GtkImContextFilterKeypress;
-        public static D.gtk_im_context_set_client_window GtkImContextSetClientWindow;
-
-        public static D.gdk_screen_get_height GdkScreenGetHeight;
-        public static D.gdk_display_get_default GdkGetDefaultDisplay;
-        public static D.gdk_screen_get_width GdkScreenGetWidth;
-        public static D.gdk_screen_get_root_window GdkScreenGetRootWindow;
-        public static D.gdk_cursor_new GdkCursorNew;
-        public static D.gdk_window_get_origin GdkWindowGetOrigin;
-        public static D.gdk_window_get_pointer GdkWindowGetPointer;
-        public static D.gdk_window_get_state GdkWindowGetState;
-        public static D.gtk_window_iconify GtkWindowIconify;
-        public static D.gtk_window_deiconify GtkWindowDeiconify;
-        public static D.gtk_window_maximize GtkWindowMaximize;
-        public static D.gtk_window_unmaximize GtkWindowUnmaximize;
-        public static D.gtk_window_close GtkWindowClose;
-        public static D.gtk_window_set_keep_above GtkWindowSetKeepAbove;
-        public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag;
-        public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag;
-        public static D.gdk_event_request_motions GdkEventRequestMotions;
-        public static D.gdk_window_process_updates GdkWindowProcessUpdates;
-        public static D.gdk_window_begin_paint_rect GdkWindowBeginPaintRect;
-        public static D.gdk_window_end_paint GdkWindowEndPaint;
-        public static D.gdk_x11_window_foreign_new_for_display GdkWindowForeignNewForDisplay;
-        public static D.gdk_window_set_transient_for GdkWindowSetTransientFor;
-        
-        public static D.gdk_pixbuf_new_from_file GdkPixbufNewFromFile;
-        public static D.gtk_icon_theme_get_default GtkIconThemeGetDefault;
-        public static D.gtk_icon_theme_load_icon GtkIconThemeLoadIcon;
-        public static D.gdk_cursor_new_from_pixbuf GdkCursorNewFromPixbuf;
-        public static D.gdk_window_set_cursor GdkWindowSetCursor;
-        public static D.gdk_pixbuf_new_from_stream GdkPixbufNewFromStream;
-        public static D.gdk_pixbuf_save_to_bufferv GdkPixbufSaveToBufferv;
-        public static D.gdk_cairo_create GdkCairoCreate;
-        
-        public static D.cairo_image_surface_create CairoImageSurfaceCreate;
-        public static D.cairo_image_surface_create_for_data CairoImageSurfaceCreateForData;
-        public static D.cairo_image_surface_get_data CairoImageSurfaceGetData;
-        public static D.cairo_image_surface_get_stride CairoImageSurfaceGetStride;
-        public static D.cairo_surface_mark_dirty CairoSurfaceMarkDirty;
-        public static D.cairo_surface_write_to_png CairoSurfaceWriteToPng;
-        public static D.cairo_surface_flush CairoSurfaceFlush;
-        public static D.cairo_surface_destroy CairoSurfaceDestroy;
-        public static D.cairo_set_source_surface CairoSetSourceSurface;
-        public static D.cairo_set_source_rgba CairoSetSourceRgba;
-        public static D.cairo_scale CairoScale;
-        public static D.cairo_paint CairoPaint;
-        public static D.cairo_show_text CairoShowText;
-        public static D.cairo_select_font_face CairoSelectFontFace;
-        public static D.cairo_set_font_size CairoSetFontSize;
-        public static D.cairo_move_to CairoMoveTo;
-        public static D.cairo_destroy CairoDestroy;
-
-        public const int G_TYPE_OBJECT = 80;
-    }
-
-    public enum GtkWindowType
-    {
-        TopLevel,
-        Popup
-    }
-
-    [StructLayout(LayoutKind.Sequential)]
-    public struct GdkRectangle
-    {
-        public int X, Y, Width, Height;
-
-        public static GdkRectangle FromRect(Rect rect)
-        {
-            return new GdkRectangle
-            {
-                X = (int) rect.X,
-                Y = (int) rect.Y,
-                Width = (int) rect.Width,
-                Height = (int) rect.Height
-            };
-        }
-    }
-
-    enum GdkEventType
-    {
-        Nothing = -1,
-        Delete = 0,
-        Destroy = 1,
-        Expose = 2,
-        MotionNotify = 3,
-        ButtonPress = 4,
-        TwoButtonPress = 5,
-        ThreeButtonPress = 6,
-        ButtonRelease = 7,
-        KeyPress = 8,
-        KeyRelease = 9,
-        EnterNotify = 10,
-        LeaveNotify = 11,
-        FocusChange = 12,
-        Configure = 13,
-        Map = 14,
-        Unmap = 15,
-        PropertyNotify = 16,
-        SelectionClear = 17,
-        SelectionRequest = 18,
-        SelectionNotify = 19,
-        ProximityIn = 20,
-        ProximityOut = 21,
-        DragEnter = 22,
-        DragLeave = 23,
-        DragMotion = 24,
-        DragStatus = 25,
-        DropStart = 26,
-        DropFinished = 27,
-        ClientEvent = 28,
-        VisibilityNotify = 29,
-        NoExpose = 30,
-        Scroll = 31,
-        WindowState = 32,
-        Setting = 33,
-        OwnerChange = 34,
-        GrabBroken = 35,
-    }
-
-    enum GdkModifierType
-    {
-        ShiftMask = 1,
-        LockMask = 2,
-        ControlMask = 4,
-        Mod1Mask = 8,
-        Mod2Mask = 16,
-        Mod3Mask = 32,
-        Mod4Mask = 64,
-        Mod5Mask = 128,
-        Button1Mask = 256,
-        Button2Mask = 512,
-        Button3Mask = 1024,
-        Button4Mask = 2048,
-        Button5Mask = 4096,
-        SuperMask = 67108864,
-        HyperMask = 134217728,
-        MetaMask = 268435456,
-        ReleaseMask = 1073741824,
-        ModifierMask = ReleaseMask | Button5Mask | Button4Mask | Button3Mask | Button2Mask | Button1Mask | Mod5Mask | Mod4Mask | Mod3Mask | Mod2Mask | Mod1Mask | ControlMask | LockMask | ShiftMask,
-        None = 0,
-    }
-
-    enum GdkScrollDirection
-    {
-        Up,
-        Down,
-        Left,
-        Right,
-        Smooth
-    }
-
-    [StructLayout(LayoutKind.Sequential)]
-    unsafe struct GdkEventButton
-    {
-        public GdkEventType type;
-        public IntPtr window;
-        public gint8 send_event;
-        public guint32 time;
-        public gdouble x;
-        public gdouble y;
-        public gdouble* axes;
-        public GdkModifierType state;
-        public guint button;
-        public IntPtr device;
-        public gdouble x_root, y_root;
-    }
-
-    [StructLayout(LayoutKind.Sequential)]
-    unsafe struct GdkEventMotion
-    {
-        public GdkEventType type;
-        public IntPtr window;
-        public gint8 send_event;
-        public guint32 time;
-        public gdouble x;
-        public gdouble y;
-        public gdouble* axes;
-        public GdkModifierType state;
-        public gint16 is_hint;
-        public IntPtr device;
-        public gdouble x_root, y_root;
-    }
-
-    [StructLayout(LayoutKind.Sequential)]
-    unsafe  struct GdkEventScroll
-    {
-        public GdkEventType type;
-        public IntPtr window;
-        public gint8 send_event;
-        public guint32 time;
-        public gdouble x;
-        public gdouble y;
-        public GdkModifierType state;
-        public GdkScrollDirection direction;
-        public IntPtr device;
-        public gdouble x_root, y_root;
-        public gdouble delta_x;
-        public gdouble delta_y;
-    }
-
-    [StructLayout(LayoutKind.Sequential)]
-    unsafe  struct GdkEventCrossing 
-    {
-        public GdkEventType type;
-        public IntPtr window;
-        public gint8 send_event;
-        public IntPtr subwindow;
-        public guint32 time;
-        public gdouble x;
-        public gdouble y;
-        public gdouble x_root;
-        public gdouble y_root;
-        public int mode;
-        public int detail;
-        public bool focus;
-        public GdkModifierType state;
-    };
-    
-    [StructLayout(LayoutKind.Sequential)]
-    unsafe struct GdkEventWindowState
-    {
-        public GdkEventType type;
-        public IntPtr window;
-        gint8 send_event;
-        public GdkWindowState changed_mask;
-        public GdkWindowState new_window_state;
-    }
-
-    [StructLayout(LayoutKind.Sequential)]
-    unsafe struct GdkEventKey
-    {
-        public GdkEventType type;
-        public IntPtr window;
-        public gint8 send_event;
-        public guint32 time;
-        public guint state;
-        public guint keyval;
-        public gint length;
-        public IntPtr pstring;
-        public guint16 hardware_keycode;
-        public byte group;
-        public guint is_modifier;
-    }
-
-    [StructLayout(LayoutKind.Sequential)]
-    unsafe struct GSList
-    {
-        public IntPtr Data;
-        public GSList* Next;
-    }
-
-    [Flags]
-    public enum GdkWindowState
-    {
-        Withdrawn = 1,
-        Iconified = 2,
-        Maximized = 4,
-        Sticky = 8,
-        Fullscreen = 16,
-        Above = 32,
-        Below = 64,
-        Focused = 128,
-        Ttiled = 256
-    }
-
-    public enum GtkResponseType
-    {
-        Help = -11,
-        Apply = -10,
-        No = -9,
-        Yes = -8,
-        Close = -7,
-        Cancel = -6,
-        Ok = -5,
-        DeleteEvent = -4,
-        Accept = -3,
-        Reject = -2,
-        None = -1,
-    }
-
-    public enum GtkFileChooserAction
-    {
-        Open,
-        Save,
-        SelectFolder,
-        CreateFolder,
-    }
-
-    [StructLayout(LayoutKind.Sequential)]
-    public struct GdkGeometry
-    {
-        public gint min_width;
-        public gint min_height;
-        public gint max_width;
-        public gint max_height;
-        public gint base_width;
-        public gint base_height;
-        public gint width_inc;
-        public gint height_inc;
-        public gdouble min_aspect;
-        public gdouble max_aspect;
-        public gint win_gravity;
-    }
-
-    enum GdkWindowHints
-    {
-        GDK_HINT_POS = 1 << 0,
-        GDK_HINT_MIN_SIZE = 1 << 1,
-        GDK_HINT_MAX_SIZE = 1 << 2,
-        GDK_HINT_BASE_SIZE = 1 << 3,
-        GDK_HINT_ASPECT = 1 << 4,
-        GDK_HINT_RESIZE_INC = 1 << 5,
-        GDK_HINT_WIN_GRAVITY = 1 << 6,
-        GDK_HINT_USER_POS = 1 << 7,
-        GDK_HINT_USER_SIZE = 1 << 8
-    }
-}

+ 0 - 20
src/Gtk/Avalonia.Gtk3/Interop/NativeException.cs

@@ -1,20 +0,0 @@
-using System;
-
-namespace Avalonia.Gtk3.Interop
-{
-    public class NativeException : Exception
-    {
-        public NativeException()
-        {
-        }
-
-        public NativeException(string message) : base(message)
-        {
-        }
-
-        public NativeException(string message, Exception inner) : base(message, inner)
-        {
-        }
-        
-    }
-}

+ 0 - 65
src/Gtk/Avalonia.Gtk3/Interop/Pixbuf.cs

@@ -1,65 +0,0 @@
-using System;
-using System.IO;
-using System.Runtime.InteropServices;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
-
-namespace Avalonia.Gtk3.Interop
-{
-    internal class Pixbuf : GObject, IWindowIconImpl
-    {
-        Pixbuf(IntPtr handle) : base(handle)
-        {
-            
-        }
-
-        public static Pixbuf NewFromFile(string filename)
-        {
-            using (var ub = new Utf8Buffer(filename))
-            {
-                IntPtr err;
-                var rv = Native.GdkPixbufNewFromFile(ub, out err);
-                if(rv != IntPtr.Zero)
-                    return new Pixbuf(rv);
-                throw new GException(err);
-            }
-        }
-
-        public static unsafe Pixbuf NewFromBytes(byte[] data)
-        {
-            fixed (void* bytes = data)
-            {
-                using (var stream = Native.GMemoryInputStreamNewFromData(new IntPtr(bytes), new IntPtr(data.Length), IntPtr.Zero))
-                {
-                    IntPtr err;
-                    var rv = Native.GdkPixbufNewFromStream(stream, IntPtr.Zero, out err);
-                    if (rv != IntPtr.Zero)
-                        return new Pixbuf(rv);
-                    throw new GException(err);
-                }
-            }
-        }
-
-        public static Pixbuf NewFromStream(Stream s)
-        {
-            if (s is MemoryStream)
-                return NewFromBytes(((MemoryStream) s).ToArray());
-            var ms = new MemoryStream();
-            s.CopyTo(ms);
-            return NewFromBytes(ms.ToArray());
-        }
-
-        public void Save(Stream outputStream)
-        {
-            IntPtr buffer, bufferLen, error;
-            using (var png = new Utf8Buffer("png"))
-                if (!Native.GdkPixbufSaveToBufferv(this, out buffer, out bufferLen, png,
-                    IntPtr.Zero, IntPtr.Zero, out error))
-                    throw new GException(error);
-            var data = new byte[bufferLen.ToInt32()];
-            Marshal.Copy(buffer, data, 0, bufferLen.ToInt32());
-            Native.GFree(buffer);
-            outputStream.Write(data, 0, data.Length);
-        }
-    }
-}

+ 0 - 156
src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs

@@ -1,156 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
-
-namespace Avalonia.Gtk3.Interop
-{
-    internal class GtkImportAttribute : Attribute
-    {
-        public GtkDll Dll { get; set; }
-        public string Name { get; set; }
-        public bool Optional { get; set; }
-
-        public GtkImportAttribute(GtkDll dll, string name = null, bool optional = false)
-        {
-            Dll = dll;
-            Name = name;
-            Optional = optional;
-        }
-    }
-
-    public enum GtkDll
-    {
-        Gdk,
-        Gtk,
-        Glib,
-        Gio,
-        Gobject,
-        Cairo,
-        GdkPixBuf
-    }
-
-    static class Resolver
-    {
-        private static Lazy<OperatingSystemType> Platform =
-            new Lazy<OperatingSystemType>(
-                () => AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().OperatingSystem);
-
-        public static ICustomGtk3NativeLibraryResolver Custom { get; set; }
-
-
-        static string FormatName(string name, int version = 0)
-        {
-            if (Platform.Value == OperatingSystemType.WinNT)
-                return "lib" + name + "-" + version + ".dll";
-            if (Platform.Value == OperatingSystemType.Linux)
-                return "lib" + name + ".so" + "." + version;
-            if (Platform.Value == OperatingSystemType.OSX)
-                return "lib" + name + "." + version + ".dylib";
-            throw new Exception("Unknown platform, use custom name resolver");
-        }
-
-        
-
-        static string GetDllName(GtkDll dll)
-        {
-            var name = Custom?.GetName(dll);
-            if (name != null)
-                return name;
-
-            switch (dll)
-            {
-                case GtkDll.Cairo:
-                    return FormatName("cairo", 2);
-                case GtkDll.Gdk:
-                    return FormatName("gdk-3");
-                case GtkDll.Glib:
-                    return FormatName("glib-2.0");
-                case GtkDll.Gio:
-                    return FormatName("gio-2.0");
-                case GtkDll.Gtk:
-                    return FormatName("gtk-3");
-                case GtkDll.Gobject:
-                    return FormatName("gobject-2.0");
-                case GtkDll.GdkPixBuf:
-                    return FormatName("gdk_pixbuf-2.0");
-                default:
-                    throw new ArgumentException("Unknown lib: " + dll);
-            }
-        }
-
-        static IntPtr LoadDll(IDynamicLibraryLoader  loader, GtkDll dll)
-        {
-            
-            var exceptions = new List<Exception>();
-
-            var name = GetDllName(dll);
-            if (Custom?.TrySystemFirst != false)
-            {
-                try
-                {
-                    return loader.LoadLibrary(name);
-                }
-                catch (Exception e)
-                {
-                    exceptions.Add(e);
-                }
-            }
-            var path = Custom?.Lookup(dll);
-            if (path == null && Custom?.BasePath != null)
-                path = Path.Combine(Custom.BasePath, name);
-            if (path != null)
-            {
-                try
-                {
-                    return loader.LoadLibrary(path);
-                }
-                catch (Exception e)
-                {
-                    exceptions.Add(e);
-                }
-            }
-            throw new AggregateException("Unable to load " + dll, exceptions);
-        }
-
-        public static void Resolve(string basePath = null)
-        {
-            var loader = AvaloniaLocator.Current.GetService<IDynamicLibraryLoader>();
-
-            var dlls = Enum.GetValues(typeof(GtkDll)).Cast<GtkDll>().ToDictionary(x => x, x => LoadDll(loader, x));
-            
-            foreach (var fieldInfo in typeof(Native).GetTypeInfo().DeclaredFields)
-            {
-                var import = fieldInfo.FieldType.GetTypeInfo().GetCustomAttributes(typeof(GtkImportAttribute), true).Cast<GtkImportAttribute>().FirstOrDefault();
-                if(import == null)
-                    continue;
-                IntPtr lib = dlls[import.Dll];
-
-                var funcPtr =  loader.GetProcAddress(lib, import.Name ?? fieldInfo.FieldType.Name, import.Optional);
-
-                if (funcPtr != IntPtr.Zero)
-                    fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(funcPtr, fieldInfo.FieldType));
-            }
-
-            var nativeHandleNames = new[] { "gdk_win32_window_get_handle", "gdk_x11_window_get_xid", "gdk_quartz_window_get_nswindow" };
-            foreach (var name in nativeHandleNames)
-            {
-                var ptr = loader.GetProcAddress(dlls[GtkDll.Gdk], name, true);
-                if (ptr == IntPtr.Zero)
-                    continue;
-                Native.GetNativeGdkWindowHandle = (Native.D.gdk_get_native_handle) Marshal
-                    .GetDelegateForFunctionPointer(ptr, typeof(Native.D.gdk_get_native_handle));
-            }
-            if (Native.GetNativeGdkWindowHandle == null)
-                throw new Exception($"Unable to locate any of [{string.Join(", ", nativeHandleNames)}] in libgdk");
-
-        }
-
-
-    }
-}
-

+ 0 - 47
src/Gtk/Avalonia.Gtk3/Interop/Signal.cs

@@ -1,47 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using Avalonia.Platform.Interop;
-
-namespace Avalonia.Gtk3.Interop
-{
-    class Signal
-    {
-        class ConnectedSignal : IDisposable
-        {
-            private readonly GObject _instance;
-            private GCHandle _handle;
-            private readonly ulong _id;
-
-            public ConnectedSignal(GObject instance, GCHandle handle, ulong id)
-            {
-                _instance = instance;
-                Native.GObjectRef(instance);
-                _handle = handle;
-                _id = id;
-            }
-
-            public void Dispose()
-            {
-                if (_handle.IsAllocated)
-                {
-                    Native.GObjectUnref(_instance.DangerousGetHandle());
-                    Native.GSignalHandlerDisconnect(_instance, _id);
-                    _handle.Free();
-                }
-            }
-        }
-
-        public static IDisposable Connect<T>(GObject obj, string name, T handler) 
-        {
-            var handle = GCHandle.Alloc(handler);
-            var ptr = Marshal.GetFunctionPointerForDelegate((Delegate)(object)handler);
-            using (var utf = new Utf8Buffer(name))
-            {
-                var id = Native.GSignalConnectObject(obj, utf, ptr, IntPtr.Zero, 0);
-                if (id == 0)
-                    throw new ArgumentException("Unable to connect to signal " + name);
-                return new ConnectedSignal(obj, handle, id);
-            }
-        }
-    }
-}

+ 0 - 230
src/Gtk/Avalonia.Gtk3/KeyTransform.cs

@@ -1,230 +0,0 @@
-using System.Collections.Generic;
-using Avalonia.Gtk3;
-using Avalonia.Input;
-
-namespace Avalonia.Gtk.Common
-{
-    static class KeyTransform
-    {
-        private static readonly Dictionary<GdkKey, Key> KeyDic = new Dictionary<GdkKey, Key>
-        {
-            { GdkKey.Cancel, Key.Cancel },
-            { GdkKey.BackSpace, Key.Back },
-            { GdkKey.Tab, Key.Tab },
-            { GdkKey.Linefeed, Key.LineFeed },
-            { GdkKey.Clear, Key.Clear },
-            { GdkKey.Return, Key.Return },
-            { GdkKey.KP_Enter, Key.Return },
-            { GdkKey.Pause, Key.Pause },
-            { GdkKey.Caps_Lock, Key.CapsLock },
-            //{ GdkKey.?, Key.HangulMode }
-            //{ GdkKey.?, Key.JunjaMode }
-            //{ GdkKey.?, Key.FinalMode }
-            //{ GdkKey.?, Key.KanjiMode }
-            { GdkKey.Escape, Key.Escape },
-            //{ GdkKey.?, Key.ImeConvert }
-            //{ GdkKey.?, Key.ImeNonConvert }
-            //{ GdkKey.?, Key.ImeAccept }
-            //{ GdkKey.?, Key.ImeModeChange }
-            { GdkKey.space, Key.Space },
-            { GdkKey.Prior, Key.Prior },
-            { GdkKey.KP_Prior, Key.Prior },
-            { GdkKey.Page_Down, Key.PageDown },
-            { GdkKey.KP_Page_Down, Key.PageDown },
-            { GdkKey.End, Key.End },
-            { GdkKey.KP_End, Key.End },
-            { GdkKey.Home, Key.Home },
-            { GdkKey.KP_Home, Key.Home },
-            { GdkKey.Left, Key.Left },
-            { GdkKey.KP_Left, Key.Left },
-            { GdkKey.Up, Key.Up },
-            { GdkKey.KP_Up, Key.Up },
-            { GdkKey.Right, Key.Right },
-            { GdkKey.KP_Right, Key.Right },
-            { GdkKey.Down, Key.Down },
-            { GdkKey.KP_Down, Key.Down },
-            { GdkKey.Select, Key.Select },
-            { GdkKey.Print, Key.Print },
-            { GdkKey.Execute, Key.Execute },
-            //{ GdkKey.?, Key.Snapshot }
-            { GdkKey.Insert, Key.Insert },
-            { GdkKey.KP_Insert, Key.Insert },
-            { GdkKey.Delete, Key.Delete },
-            { GdkKey.KP_Delete, Key.Delete },
-            { GdkKey.Help, Key.Help },
-            { GdkKey.Key_0, Key.D0 },
-            { GdkKey.Key_1, Key.D1 },
-            { GdkKey.Key_2, Key.D2 },
-            { GdkKey.Key_3, Key.D3 },
-            { GdkKey.Key_4, Key.D4 },
-            { GdkKey.Key_5, Key.D5 },
-            { GdkKey.Key_6, Key.D6 },
-            { GdkKey.Key_7, Key.D7 },
-            { GdkKey.Key_8, Key.D8 },
-            { GdkKey.Key_9, Key.D9 },
-            { GdkKey.A, Key.A },
-            { GdkKey.B, Key.B },
-            { GdkKey.C, Key.C },
-            { GdkKey.D, Key.D },
-            { GdkKey.E, Key.E },
-            { GdkKey.F, Key.F },
-            { GdkKey.G, Key.G },
-            { GdkKey.H, Key.H },
-            { GdkKey.I, Key.I },
-            { GdkKey.J, Key.J },
-            { GdkKey.K, Key.K },
-            { GdkKey.L, Key.L },
-            { GdkKey.M, Key.M },
-            { GdkKey.N, Key.N },
-            { GdkKey.O, Key.O },
-            { GdkKey.P, Key.P },
-            { GdkKey.Q, Key.Q },
-            { GdkKey.R, Key.R },
-            { GdkKey.S, Key.S },
-            { GdkKey.T, Key.T },
-            { GdkKey.U, Key.U },
-            { GdkKey.V, Key.V },
-            { GdkKey.W, Key.W },
-            { GdkKey.X, Key.X },
-            { GdkKey.Y, Key.Y },
-            { GdkKey.Z, Key.Z },
-            { GdkKey.a, Key.A },
-            { GdkKey.b, Key.B },
-            { GdkKey.c, Key.C },
-            { GdkKey.d, Key.D },
-            { GdkKey.e, Key.E },
-            { GdkKey.f, Key.F },
-            { GdkKey.g, Key.G },
-            { GdkKey.h, Key.H },
-            { GdkKey.i, Key.I },
-            { GdkKey.j, Key.J },
-            { GdkKey.k, Key.K },
-            { GdkKey.l, Key.L },
-            { GdkKey.m, Key.M },
-            { GdkKey.n, Key.N },
-            { GdkKey.o, Key.O },
-            { GdkKey.p, Key.P },
-            { GdkKey.q, Key.Q },
-            { GdkKey.r, Key.R },
-            { GdkKey.s, Key.S },
-            { GdkKey.t, Key.T },
-            { GdkKey.u, Key.U },
-            { GdkKey.v, Key.V },
-            { GdkKey.w, Key.W },
-            { GdkKey.x, Key.X },
-            { GdkKey.y, Key.Y },
-            { GdkKey.z, Key.Z },
-            //{ GdkKey.?, Key.LWin }
-            //{ GdkKey.?, Key.RWin }
-            { GdkKey.Menu, Key.Apps },
-            //{ GdkKey.?, Key.Sleep }
-            { GdkKey.KP_0, Key.NumPad0 },
-            { GdkKey.KP_1, Key.NumPad1 },
-            { GdkKey.KP_2, Key.NumPad2 },
-            { GdkKey.KP_3, Key.NumPad3 },
-            { GdkKey.KP_4, Key.NumPad4 },
-            { GdkKey.KP_5, Key.NumPad5 },
-            { GdkKey.KP_6, Key.NumPad6 },
-            { GdkKey.KP_7, Key.NumPad7 },
-            { GdkKey.KP_8, Key.NumPad8 },
-            { GdkKey.KP_9, Key.NumPad9 },
-            { GdkKey.multiply, Key.Multiply },
-            { GdkKey.KP_Multiply, Key.Multiply },
-            { GdkKey.KP_Add, Key.Add },
-            //{ GdkKey.?, Key.Separator }
-            { GdkKey.KP_Subtract, Key.Subtract },
-            { GdkKey.KP_Decimal, Key.Decimal },
-            { GdkKey.KP_Divide, Key.Divide },
-            { GdkKey.F1, Key.F1 },
-            { GdkKey.F2, Key.F2 },
-            { GdkKey.F3, Key.F3 },
-            { GdkKey.F4, Key.F4 },
-            { GdkKey.F5, Key.F5 },
-            { GdkKey.F6, Key.F6 },
-            { GdkKey.F7, Key.F7 },
-            { GdkKey.F8, Key.F8 },
-            { GdkKey.F9, Key.F9 },
-            { GdkKey.F10, Key.F10 },
-            { GdkKey.F11, Key.F11 },
-            { GdkKey.F12, Key.F12 },
-            { GdkKey.L3, Key.F13 },
-            { GdkKey.F14, Key.F14 },
-            { GdkKey.L5, Key.F15 },
-            { GdkKey.F16, Key.F16 },
-            { GdkKey.F17, Key.F17 },
-            { GdkKey.L8, Key.F18 },
-            { GdkKey.L9, Key.F19 },
-            { GdkKey.L10, Key.F20 },
-            { GdkKey.R1, Key.F21 },
-            { GdkKey.R2, Key.F22 },
-            { GdkKey.F23, Key.F23 },
-            { GdkKey.R4, Key.F24 },
-            { GdkKey.Num_Lock, Key.NumLock },
-            { GdkKey.Scroll_Lock, Key.Scroll },
-            { GdkKey.Shift_L, Key.LeftShift },
-            { GdkKey.Shift_R, Key.RightShift },
-            { GdkKey.Control_L, Key.LeftCtrl },
-            { GdkKey.Control_R, Key.RightCtrl },
-            { GdkKey.Alt_L, Key.LeftAlt },
-            { GdkKey.Alt_R, Key.RightAlt },
-            //{ GdkKey.?, Key.BrowserBack }
-            //{ GdkKey.?, Key.BrowserForward }
-            //{ GdkKey.?, Key.BrowserRefresh }
-            //{ GdkKey.?, Key.BrowserStop }
-            //{ GdkKey.?, Key.BrowserSearch }
-            //{ GdkKey.?, Key.BrowserFavorites }
-            //{ GdkKey.?, Key.BrowserHome }
-            //{ GdkKey.?, Key.VolumeMute }
-            //{ GdkKey.?, Key.VolumeDown }
-            //{ GdkKey.?, Key.VolumeUp }
-            //{ GdkKey.?, Key.MediaNextTrack }
-            //{ GdkKey.?, Key.MediaPreviousTrack }
-            //{ GdkKey.?, Key.MediaStop }
-            //{ GdkKey.?, Key.MediaPlayPause }
-            //{ GdkKey.?, Key.LaunchMail }
-            //{ GdkKey.?, Key.SelectMedia }
-            //{ GdkKey.?, Key.LaunchApplication1 }
-            //{ GdkKey.?, Key.LaunchApplication2 }
-            { GdkKey.semicolon, Key.OemSemicolon },
-            { GdkKey.plus, Key.OemPlus },
-            { GdkKey.equal, Key.OemPlus },
-            { GdkKey.comma, Key.OemComma },
-            { GdkKey.minus, Key.OemMinus },
-            { GdkKey.period, Key.OemPeriod },
-            { GdkKey.slash, Key.Oem2 },
-            { GdkKey.grave, Key.OemTilde },
-            //{ GdkKey.?, Key.AbntC1 }
-            //{ GdkKey.?, Key.AbntC2 }
-            { GdkKey.bracketleft, Key.OemOpenBrackets },
-            { GdkKey.backslash, Key.OemPipe },
-            { GdkKey.bracketright, Key.OemCloseBrackets },
-            { GdkKey.apostrophe, Key.OemQuotes },
-            //{ GdkKey.?, Key.Oem8 }
-            //{ GdkKey.?, Key.Oem102 }
-            //{ GdkKey.?, Key.ImeProcessed }
-            //{ GdkKey.?, Key.System }
-            //{ GdkKey.?, Key.OemAttn }
-            //{ GdkKey.?, Key.OemFinish }
-            //{ GdkKey.?, Key.DbeHiragana }
-            //{ GdkKey.?, Key.OemAuto }
-            //{ GdkKey.?, Key.DbeDbcsChar }
-            //{ GdkKey.?, Key.OemBackTab }
-            //{ GdkKey.?, Key.Attn }
-            //{ GdkKey.?, Key.DbeEnterWordRegisterMode }
-            //{ GdkKey.?, Key.DbeEnterImeConfigureMode }
-            //{ GdkKey.?, Key.EraseEof }
-            //{ GdkKey.?, Key.Play }
-            //{ GdkKey.?, Key.Zoom }
-            //{ GdkKey.?, Key.NoName }
-            //{ GdkKey.?, Key.DbeEnterDialogConversionMode }
-            //{ GdkKey.?, Key.OemClear }
-            //{ GdkKey.?, Key.DeadCharProcessed }
-        };
-
-        public static Key ConvertKey(GdkKey key)
-        {
-            Key result;
-            return KeyDic.TryGetValue(key, out result) ? result : Key.None;
-        }
-    }
-}

+ 0 - 20
src/Gtk/Avalonia.Gtk3/PlatformIconLoader.cs

@@ -1,20 +0,0 @@
-using System.IO;
-using Avalonia.Gtk3.Interop;
-using Avalonia.Platform;
-
-namespace Avalonia.Gtk3
-{
-    class PlatformIconLoader : IPlatformIconLoader
-    {
-        public IWindowIconImpl LoadIcon(string fileName) => Pixbuf.NewFromFile(fileName);
-
-        public IWindowIconImpl LoadIcon(Stream stream) => Pixbuf.NewFromStream(stream);
-
-        public IWindowIconImpl LoadIcon(IBitmapImpl bitmap)
-        {
-            var ms = new MemoryStream();
-            bitmap.Save(ms);
-            return Pixbuf.NewFromBytes(ms.ToArray());
-        }
-    }
-}

+ 0 - 19
src/Gtk/Avalonia.Gtk3/PopupImpl.cs

@@ -1,19 +0,0 @@
-using Avalonia.Gtk3.Interop;
-using Avalonia.Platform;
-
-namespace Avalonia.Gtk3
-{
-    class PopupImpl : WindowBaseImpl, IPopupImpl
-    {
-        static GtkWindow CreateWindow()
-        {
-            var window = Native.GtkWindowNew(GtkWindowType.Popup);
-            return window;
-        }
-
-        public PopupImpl() : base(CreateWindow())
-        {
-            OverrideRedirect = true;
-        }
-    }
-}

+ 0 - 10
src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs

@@ -1,10 +0,0 @@
-using System.Reflection;
-using Avalonia.Gtk3;
-using Avalonia.Platform;
-
-// 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: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
-[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
-[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]

+ 0 - 8
src/Gtk/Avalonia.Gtk3/README.md

@@ -1,8 +0,0 @@
-P/Invoke based GTK3 backend
-===========================
-
-Code is EXPERIMENTAL at this point. It also needs Direct2D/Skia for rendering.
-
-Windows GTK3 binaries aren't included in the repo, you need to download them from https://sourceforge.net/projects/gtk3win/
-
-On Linux it should work out of the box with system-provided GTK3. On OSX you should be able to wire GTK3 using DYLD_LIBRARY_PATH environment variable.

+ 0 - 56
src/Gtk/Avalonia.Gtk3/ScreenImpl.cs

@@ -1,56 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Gtk3.Interop;
-using Avalonia.Platform;
-
-namespace Avalonia.Gtk3
-{
-    internal class ScreenImpl : IScreenImpl
-    {
-        public int ScreenCount
-        {
-            get => _allScreens.Length;
-        }
-        
-        private Screen[] _allScreens;
-        public IReadOnlyList<Screen> AllScreens
-        {
-            get
-            {
-                if (_allScreens == null)
-                {
-                    IntPtr display = Native.GdkGetDefaultDisplay();
-                    GdkScreen screen = Native.GdkDisplayGetDefaultScreen(display);
-                    short primary = Native.GdkScreenGetPrimaryMonitor(screen);
-                    Screen[] screens = new Screen[Native.GdkScreenGetNMonitors(screen)];
-                    for (short i = 0; i < screens.Length; i++)
-                    {
-                        GdkRectangle workArea = new GdkRectangle(), geometry = new GdkRectangle();
-                        Native.GdkScreenGetMonitorGeometry(screen, i, ref geometry);
-                        Native.GdkScreenGetMonitorWorkarea(screen, i, ref workArea);
-                        PixelRect workAreaRect = new PixelRect(workArea.X, workArea.Y, workArea.Width, workArea.Height);
-                        PixelRect geometryRect = new PixelRect(geometry.X, geometry.Y, geometry.Width, geometry.Height);
-                        GtkScreen s = new GtkScreen(geometryRect, workAreaRect, i == primary, i);
-                        screens[i] = s;
-                    }
-
-                    _allScreens = screens;
-                }
-
-                return _allScreens;
-            }
-        }
-
-        public ScreenImpl()
-        {
-            IntPtr display = Native.GdkGetDefaultDisplay();
-            GdkScreen screen = Native.GdkDisplayGetDefaultScreen(display);
-            Signal.Connect<Native.D.monitors_changed>(screen, "monitors-changed", MonitorsChanged);
-        }
-
-        private unsafe void MonitorsChanged(IntPtr screen, IntPtr userData)
-        {
-            _allScreens = null;
-        }
-    }
-}

+ 0 - 110
src/Gtk/Avalonia.Gtk3/SystemDialogs.cs

@@ -1,110 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Avalonia.Controls;
-using Avalonia.Controls.Platform;
-using Avalonia.Gtk3.Interop;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
-
-namespace Avalonia.Gtk3
-{
-    class SystemDialogBase
-    {
-
-        public unsafe static Task<string[]> ShowDialog(string title, GtkWindow parent, GtkFileChooserAction action,
-            bool multiselect, string initialFileName, Action<GtkFileChooser> modify)
-        {
-            GtkFileChooser dlg;
-            parent = parent ?? GtkWindow.Null;
-            using (var name = new Utf8Buffer(title))
-                dlg = Native.GtkFileChooserDialogNew(name, parent, action, IntPtr.Zero);
-            modify?.Invoke(dlg);
-            if (multiselect)
-                Native.GtkFileChooserSetSelectMultiple(dlg, true);
-
-            Native.GtkWindowSetModal(dlg, true);
-            var tcs = new TaskCompletionSource<string[]>();
-            List<IDisposable> disposables = null;
-            Action dispose = () =>
-            {
-                // ReSharper disable once PossibleNullReferenceException
-                foreach (var d in disposables)
-                    d.Dispose();
-                disposables.Clear();
-            };
-            disposables = new List<IDisposable>
-            {
-                Signal.Connect<Native.D.signal_generic>(dlg, "close", delegate
-                {
-                    tcs.TrySetResult(null);
-                    dispose();
-                    return false;
-                }),
-                Signal.Connect<Native.D.signal_dialog_response>(dlg, "response", (_, resp, __) =>
-                {
-                    string[] result = null;
-                    if (resp == GtkResponseType.Accept)
-                    {
-                        var rlst = new List<string>();
-                        var gs = Native.GtkFileChooserGetFilenames(dlg);
-                        var cgs = gs;
-                        while (cgs != null)
-                        {
-                            if (cgs->Data != IntPtr.Zero)
-                                rlst.Add(Utf8Buffer.StringFromPtr(cgs->Data));
-                            cgs = cgs->Next;
-                        }
-
-                        Native.GSlistFree(gs);
-                        result = rlst.ToArray();
-                    }
-
-                    Native.GtkWidgetHide(dlg);
-                    dispose();
-                    tcs.TrySetResult(result);
-                    return false;
-                }),
-                dlg
-            };
-            using (var open = new Utf8Buffer("Open"))
-                Native.GtkDialogAddButton(dlg, open, GtkResponseType.Accept);
-            using (var open = new Utf8Buffer("Cancel"))
-                Native.GtkDialogAddButton(dlg, open, GtkResponseType.Cancel);
-            if (initialFileName != null)
-                using (var fn = new Utf8Buffer(initialFileName))
-                    Native.GtkFileChooserSetFilename(dlg, fn);
-            Native.GtkWindowPresent(dlg);
-            return tcs.Task;
-        }
-
-        public Task<string[]> ShowFileDialogAsync(FileDialog dialog, GtkWindow parent,
-            Action<GtkFileChooser> modify = null)
-        {
-            return ShowDialog(dialog.Title, parent,
-                dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save,
-                (dialog as OpenFileDialog)?.AllowMultiple ?? false,
-                Path.Combine(string.IsNullOrEmpty(dialog.InitialDirectory) ? "" : dialog.InitialDirectory,
-                    string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), modify);
-        }
-
-        public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, GtkWindow parent,
-            Action<GtkFileChooser> modify = null)
-        {
-            var res = await ShowDialog(dialog.Title, parent,
-                GtkFileChooserAction.SelectFolder, false, dialog.InitialDirectory, modify);
-            return res?.FirstOrDefault();
-        }
-    }
-
-    class SystemDialog : SystemDialogBase, ISystemDialogImpl
-    {
-        public Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
-            => ShowFolderDialogAsync(dialog, ((WindowBaseImpl)parent)?.GtkWidget);
-
-        public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
-            => ShowFileDialogAsync(dialog, ((WindowBaseImpl)parent)?.GtkWidget);
-    }
-}

+ 0 - 527
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@@ -1,527 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Threading;
-using Avalonia.Controls;
-using Avalonia.Gtk3.Interop;
-using Avalonia.Input;
-using Avalonia.Input.Raw;
-using Avalonia.OpenGL;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
-using Avalonia.Rendering;
-using Avalonia.Threading;
-
-namespace Avalonia.Gtk3
-{
-    abstract class WindowBaseImpl : IWindowBaseImpl, IPlatformHandle, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
-    {
-        public readonly GtkWindow GtkWidget;
-        private IInputRoot _inputRoot;
-        private readonly GtkImContext _imContext;
-        private readonly FramebufferManager _framebuffer;
-        private readonly EglGlPlatformSurface _egl;
-        protected readonly List<IDisposable> Disposables = new List<IDisposable>();
-        private Size _lastSize;
-        private PixelPoint _lastPosition;
-        private double _lastScaling;
-        private uint _lastKbdEvent;
-        private uint _lastSmoothScrollEvent;
-        private GCHandle _gcHandle;
-        private object _lock = new object();
-        private IDeferredRenderOperation _nextRenderOperation;
-        private readonly AutoResetEvent _canSetNextOperation = new AutoResetEvent(true);
-        internal IntPtr? GdkWindowHandle;
-        private bool _overrideRedirect;
-        private uint? _tickCallback;
-        public WindowBaseImpl(GtkWindow gtkWidget)
-        {
-            
-            GtkWidget = gtkWidget;
-            
-            var glf = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>() as EglGlPlatformFeature;
-            if (glf != null)
-                _egl = new EglGlPlatformSurface((EglDisplay)glf.Display, glf.DeferredContext, this);
-            else
-                _framebuffer = new FramebufferManager(this);
-            
-            _imContext = Native.GtkImMulticontextNew();
-            Disposables.Add(_imContext);
-            Native.GtkWidgetSetEvents(gtkWidget, 0xFFFFFE);
-            Disposables.Add(Signal.Connect<Native.D.signal_commit>(_imContext, "commit", OnCommit));
-            Connect<Native.D.signal_widget_draw>("draw", OnDraw);
-            Connect<Native.D.signal_generic>("realize", OnRealized);
-            ConnectEvent("configure-event", OnConfigured);
-            ConnectEvent("button-press-event", OnButton);
-            ConnectEvent("button-release-event", OnButton);
-            ConnectEvent("motion-notify-event", OnMotion);
-            ConnectEvent("scroll-event", OnScroll);
-            ConnectEvent("window-state-event", OnStateChanged);
-            ConnectEvent("key-press-event", OnKeyEvent);
-            ConnectEvent("key-release-event", OnKeyEvent);
-            ConnectEvent("leave-notify-event", OnLeaveNotifyEvent);
-            ConnectEvent("delete-event", OnClosingEvent);
-            Connect<Native.D.signal_generic>("destroy", OnDestroy);
-            Native.GtkWidgetRealize(gtkWidget);
-            GdkWindowHandle = this.Handle.Handle;
-            _lastSize = ClientSize;
-
-            if (_egl != null)
-                Native.GtkWidgetSetDoubleBuffered(gtkWidget, false);
-            else if (Gtk3Platform.UseDeferredRendering)
-            {
-                Native.GtkWidgetSetDoubleBuffered(gtkWidget, false);
-                _gcHandle = GCHandle.Alloc(this);
-                _tickCallback = Native.GtkWidgetAddTickCallback(GtkWidget, PinnedStaticCallback,
-                    GCHandle.ToIntPtr(_gcHandle), IntPtr.Zero);
-            }
-        }
-
-        private bool OnConfigured(IntPtr gtkwidget, IntPtr ev, IntPtr userdata)
-        {
-            int w, h;
-            if (!OverrideRedirect)
-            {
-                Native.GtkWindowGetSize(GtkWidget, out w, out h);
-                var size = ClientSize = new Size(w, h);
-                if (_lastSize != size)
-                {
-                    Resized?.Invoke(size);
-                    _lastSize = size;
-                }
-            }
-            var pos = Position;
-            if (_lastPosition != pos)
-            {
-                PositionChanged?.Invoke(pos);
-                _lastPosition = pos;
-            }
-            var scaling = Scaling;
-            if (_lastScaling != scaling)
-            {
-                ScalingChanged?.Invoke(scaling);
-                _lastScaling = scaling;
-            }
-            return false;
-        }
-
-        private bool OnRealized(IntPtr gtkwidget, IntPtr userdata)
-        {
-            Native.GtkImContextSetClientWindow(_imContext, Native.GtkWidgetGetWindow(GtkWidget));
-            return false;
-        }
-
-        private bool OnDestroy(IntPtr gtkwidget, IntPtr userdata)
-        {
-            DoDispose(true);
-            return false;
-        }
-
-        private static InputModifiers GetModifierKeys(GdkModifierType state)
-        {
-            var rv = InputModifiers.None;
-            if (state.HasFlag(GdkModifierType.ControlMask))
-                rv |= InputModifiers.Control;
-            if (state.HasFlag(GdkModifierType.ShiftMask))
-                rv |= InputModifiers.Shift;
-            if (state.HasFlag(GdkModifierType.Mod1Mask))
-                rv |= InputModifiers.Alt;
-            if (state.HasFlag(GdkModifierType.Button1Mask))
-                rv |= InputModifiers.LeftMouseButton;
-            if (state.HasFlag(GdkModifierType.Button2Mask))
-                rv |= InputModifiers.RightMouseButton;
-            if (state.HasFlag(GdkModifierType.Button3Mask))
-                rv |= InputModifiers.MiddleMouseButton;
-            return rv;
-        }
-
-        private unsafe bool OnClosingEvent(IntPtr w, IntPtr ev, IntPtr userdata)
-        {
-            bool? preventClosing = Closing?.Invoke();
-            return preventClosing ?? false;
-        }
-
-        private unsafe bool OnButton(IntPtr w, IntPtr ev, IntPtr userdata)
-        {
-            var evnt = (GdkEventButton*)ev;
-            var e = new RawPointerEventArgs(
-                Gtk3Platform.Mouse,
-                evnt->time,
-                _inputRoot,
-                evnt->type == GdkEventType.ButtonRelease
-                    ? evnt->button == 1
-                        ? RawPointerEventType.LeftButtonUp
-                        : evnt->button == 3 ? RawPointerEventType.RightButtonUp : RawPointerEventType.MiddleButtonUp
-                    : evnt->button == 1
-                        ? RawPointerEventType.LeftButtonDown
-                        : evnt->button == 3 ? RawPointerEventType.RightButtonDown : RawPointerEventType.MiddleButtonDown,
-                new Point(evnt->x, evnt->y), GetModifierKeys(evnt->state));
-            OnInput(e);
-            return true;
-        }
-
-        protected virtual unsafe bool OnStateChanged(IntPtr w, IntPtr pev, IntPtr userData)
-        {
-            var ev = (GdkEventWindowState*) pev;
-            if (ev->changed_mask.HasFlag(GdkWindowState.Focused))
-            {
-                if(ev->new_window_state.HasFlag(GdkWindowState.Focused))
-                    Activated?.Invoke();
-                else
-                    Deactivated?.Invoke();
-            }
-            return true;
-        }
-
-        private unsafe bool OnMotion(IntPtr w, IntPtr ev, IntPtr userdata)
-        {
-            var evnt = (GdkEventMotion*)ev;
-            var position = new Point(evnt->x, evnt->y);
-            Native.GdkEventRequestMotions(ev);
-            var e = new RawPointerEventArgs(
-                Gtk3Platform.Mouse,
-                evnt->time,
-                _inputRoot,
-                RawPointerEventType.Move,
-                position, GetModifierKeys(evnt->state));
-            OnInput(e);
-            
-            return true;
-        }
-        private unsafe bool OnScroll(IntPtr w, IntPtr ev, IntPtr userdata)
-        {
-            var evnt = (GdkEventScroll*)ev;
-
-            //Ignore duplicates
-            if (evnt->time - _lastSmoothScrollEvent < 10 && evnt->direction != GdkScrollDirection.Smooth)
-                return true;
-
-            var delta = new Vector();
-            const double step = (double) 1;
-            if (evnt->direction == GdkScrollDirection.Down)
-                delta = new Vector(0, -step);
-            else if (evnt->direction == GdkScrollDirection.Up)
-                delta = new Vector(0, step);
-            else if (evnt->direction == GdkScrollDirection.Right)
-                delta = new Vector(-step, 0);
-            else if (evnt->direction == GdkScrollDirection.Left)
-                delta = new Vector(step, 0);
-            else if (evnt->direction == GdkScrollDirection.Smooth)
-            {
-                delta = new Vector(-evnt->delta_x, -evnt->delta_y);
-                _lastSmoothScrollEvent = evnt->time;
-            }
-            var e = new RawMouseWheelEventArgs(Gtk3Platform.Mouse, evnt->time, _inputRoot,
-                new Point(evnt->x, evnt->y), delta, GetModifierKeys(evnt->state));
-            OnInput(e);
-            return true;
-        }
-
-        private unsafe bool OnKeyEvent(IntPtr w, IntPtr pev, IntPtr userData)
-        {
-            var evnt = (GdkEventKey*) pev;
-            _lastKbdEvent = evnt->time;
-            var e = new RawKeyEventArgs(
-                Gtk3Platform.Keyboard,
-                evnt->time,
-                evnt->type == GdkEventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
-                Avalonia.Gtk.Common.KeyTransform.ConvertKey((GdkKey)evnt->keyval), GetModifierKeys((GdkModifierType)evnt->state));
-            OnInput(e);
-            if (Native.GtkImContextFilterKeypress(_imContext, pev))
-                return true;
-            return true;
-        }
-
-        private unsafe bool OnLeaveNotifyEvent(IntPtr w, IntPtr pev, IntPtr userData)
-        {
-            var evnt = (GdkEventCrossing*) pev;
-            var position = new Point(evnt->x, evnt->y);
-            OnInput(new RawPointerEventArgs(Gtk3Platform.Mouse,
-                evnt->time,
-                _inputRoot,
-                RawPointerEventType.Move,
-                position, GetModifierKeys(evnt->state)));
-            return true;
-        }
-
-        private unsafe bool OnCommit(IntPtr gtkwidget, IntPtr utf8string, IntPtr userdata)
-        {
-            OnInput(new RawTextInputEventArgs(Gtk3Platform.Keyboard, _lastKbdEvent, Utf8Buffer.StringFromPtr(utf8string)));
-            return true;
-        }
-
-        protected void ConnectEvent(string name, Native.D.signal_onevent handler) 
-            => Disposables.Add(Signal.Connect<Native.D.signal_onevent>(GtkWidget, name, handler));
-        void Connect<T>(string name, T handler) => Disposables.Add(Signal.Connect(GtkWidget, name, handler));
-
-        internal IntPtr CurrentCairoContext { get; private set; }
-
-        private bool OnDraw(IntPtr gtkwidget, IntPtr cairocontext, IntPtr userdata)
-        {
-            if (!Gtk3Platform.UseDeferredRendering)
-            {
-                CurrentCairoContext = cairocontext;
-                Paint?.Invoke(new Rect(ClientSize));
-                CurrentCairoContext = IntPtr.Zero;
-            }
-            else
-                Paint?.Invoke(new Rect(ClientSize));
-            return true;
-        }
-
-        private static Native.D.TickCallback PinnedStaticCallback = StaticTickCallback;
-
-        static bool StaticTickCallback(IntPtr widget, IntPtr clock, IntPtr userData)
-        {
-            var impl = (WindowBaseImpl) GCHandle.FromIntPtr(userData).Target;
-            impl.OnRenderTick();
-            return true;
-        }
-
-        public void SetNextRenderOperation(IDeferredRenderOperation op)
-        {
-            while (true)
-            {
-                lock (_lock)
-                {
-                    if (_nextRenderOperation == null)
-                    {
-                        _nextRenderOperation = op;
-                        return;
-                    }
-                }
-                _canSetNextOperation.WaitOne();
-            }
-            
-        }
-
-        private void OnRenderTick()
-        {
-            IDeferredRenderOperation op = null;
-            lock (_lock)
-            {
-                if (_nextRenderOperation != null)
-                {
-                    op = _nextRenderOperation;
-                    _nextRenderOperation = null;
-                }
-                _canSetNextOperation.Set();
-            }
-            if (op != null)
-            {
-                op?.RenderNow(null);
-                op?.Dispose();
-            }
-        }
-
-
-        public void Dispose() => DoDispose(false);
-        
-        void DoDispose(bool fromDestroy)
-        {
-            if (_tickCallback.HasValue)
-            {
-                if (!GtkWidget.IsClosed)
-                    Native.GtkWidgetRemoveTickCallback(GtkWidget, _tickCallback.Value);
-                _tickCallback = null;
-            }
-            
-            //We are calling it here, since signal handler will be detached
-            if (!GtkWidget.IsClosed)
-                Closed?.Invoke();
-            foreach(var d in Disposables.AsEnumerable().Reverse())
-                d.Dispose();
-            Disposables.Clear();
-            
-            if (!fromDestroy && !GtkWidget.IsClosed)
-                Native.GtkWindowClose(GtkWidget);
-            GtkWidget.Dispose();
-            
-            if (_gcHandle.IsAllocated)
-            {
-                _gcHandle.Free();
-            }
-        }
-
-        public Size MaxClientSize
-        {
-            get
-            {
-                var s = Native.GtkWidgetGetScreen(GtkWidget);
-                return new Size(Native.GdkScreenGetWidth(s), Native.GdkScreenGetHeight(s));
-            }
-        }
-
-        public void SetMinMaxSize(Size minSize, Size maxSize)
-        {
-            if (GtkWidget.IsClosed)
-                return;
-
-            GdkGeometry geometry = new GdkGeometry();
-            geometry.min_width = minSize.Width > 0 ? (int)minSize.Width : -1;
-            geometry.min_height = minSize.Height > 0 ? (int)minSize.Height : -1;
-            geometry.max_width = !Double.IsInfinity(maxSize.Width) && maxSize.Width > 0 ? (int)maxSize.Width : 999999;
-            geometry.max_height = !Double.IsInfinity(maxSize.Height) && maxSize.Height > 0 ? (int)maxSize.Height : 999999;
-
-            Native.GtkWindowSetGeometryHints(GtkWidget, IntPtr.Zero, ref geometry, GdkWindowHints.GDK_HINT_MIN_SIZE | GdkWindowHints.GDK_HINT_MAX_SIZE);
-        } 
-
-        public IMouseDevice MouseDevice => Gtk3Platform.Mouse;
-
-        public double Scaling => LastKnownScaleFactor = (int) (Native.GtkWidgetGetScaleFactor?.Invoke(GtkWidget) ?? 1);
-
-        public IPlatformHandle Handle => this;
-
-        string IPlatformHandle.HandleDescriptor => "HWND";
-
-        public Action Activated { get; set; }
-        public Func<bool> Closing { 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; } //TODO
-        public Action<PixelPoint> PositionChanged { get; set; }
-
-        public void Activate() => Native.GtkWidgetActivate(GtkWidget);
-
-        public void Invalidate(Rect rect)
-        {
-            if(GtkWidget.IsClosed)
-                return;
-            var s = ClientSize;
-            Native.GtkWidgetQueueDrawArea(GtkWidget, 0, 0, (int) s.Width, (int) s.Height);
-        }
-
-        public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
-
-        void OnInput(RawInputEventArgs args)
-        {
-            Dispatcher.UIThread.Post(() => Input?.Invoke(args), DispatcherPriority.Input);
-        }
-
-        public Point PointToClient(PixelPoint point)
-        {
-            int x, y;
-            Native.GdkWindowGetOrigin(Native.GtkWidgetGetWindow(GtkWidget), out x, out y);
-
-            return new Point(point.X - x, point.Y - y);
-        }
-
-        public PixelPoint PointToScreen(Point point)
-        {
-            int x, y;
-            Native.GdkWindowGetOrigin(Native.GtkWidgetGetWindow(GtkWidget), out x, out y);
-            return new PixelPoint((int)(point.X + x), (int)(point.Y + y));
-        }
-
-        public void SetCursor(IPlatformHandle cursor)
-        {
-            if (GtkWidget.IsClosed)
-                return;
-            Native.GdkWindowSetCursor(Native.GtkWidgetGetWindow(GtkWidget), cursor?.Handle ??  IntPtr.Zero);
-        }
-
-        public virtual void Show() => Native.GtkWindowPresent(GtkWidget);
-
-        public void Hide() => Native.GtkWidgetHide(GtkWidget);
-
-        public void SetTopmost(bool value) => Native.GtkWindowSetKeepAbove(GtkWidget, value);
-
-        void GetGlobalPointer(out int x, out int y)
-        {
-            int mask;
-            Native.GdkWindowGetPointer(Native.GdkScreenGetRootWindow(Native.GtkWidgetGetScreen(GtkWidget)),
-                out x, out y, out mask);
-        }
-
-        public void BeginMoveDrag()
-        {
-            int x, y;
-            GetGlobalPointer(out x, out y);
-            Native.GdkWindowBeginMoveDrag(Native.GtkWidgetGetWindow(GtkWidget), 1, x, y, 0);
-        }
-
-        public void BeginResizeDrag(WindowEdge edge)
-        {
-            int x, y;
-            GetGlobalPointer(out x, out y);
-            Native.GdkWindowBeginResizeDrag(Native.GtkWidgetGetWindow(GtkWidget), edge, 1, x, y, 0);
-        }
-
-
-        public Size ClientSize { get; private set; }
-        public int LastKnownScaleFactor { get; private set; }
-
-        public void Resize(Size value)
-        {
-            if (GtkWidget.IsClosed)
-                return;
-         
-            Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height);
-            if (OverrideRedirect)
-            {
-                var size = ClientSize = value;
-                if (_lastSize != size)
-                {
-                    Resized?.Invoke(size);
-                    _lastSize = size;
-                }
-            }
-        }
-
-        public bool OverrideRedirect
-        {
-            get => _overrideRedirect;
-            set
-            {
-                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
-                {
-                    Native.GdkWindowSetOverrideRedirect(Native.GtkWidgetGetWindow(GtkWidget), value);
-                    _overrideRedirect = value;
-                }
-            }
-        }
-        
-        public IScreenImpl Screen
-        {
-            get;
-        } = new ScreenImpl();
-
-        public PixelPoint Position
-        {
-            get
-            {
-                int x, y;
-                Native.GtkWindowGetPosition(GtkWidget, out x, out y);
-                return new PixelPoint(x, y);
-            }
-            set { Native.GtkWindowMove(GtkWidget, (int)value.X, (int)value.Y); }
-        }
-
-        IntPtr IPlatformHandle.Handle => Native.GetNativeGdkWindowHandle(Native.GtkWidgetGetWindow(GtkWidget));
-        public IEnumerable<object> Surfaces => new object[] {Handle, _egl, _framebuffer};
-
-        public IRenderer CreateRenderer(IRenderRoot root)
-        {
-            var loop = AvaloniaLocator.Current.GetService<IRenderLoop>();
-            return Gtk3Platform.UseDeferredRendering
-                ? (IRenderer) new DeferredRenderer(root, loop)
-                : new ImmediateRenderer(root);
-        }
-
-        PixelSize EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Size
-        {
-            get
-            {
-                var cs = ClientSize;
-                return new PixelSize((int)Math.Max(1, LastKnownScaleFactor * cs.Width),
-                    (int)Math.Max(1, LastKnownScaleFactor * ClientSize.Height));
-            }
-        }
-        double EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Scaling => LastKnownScaleFactor;
-        IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle;
-    }
-}

+ 0 - 102
src/Gtk/Avalonia.Gtk3/WindowImpl.cs

@@ -1,102 +0,0 @@
-using System;
-using Avalonia.Controls;
-using Avalonia.Gtk3.Interop;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
-
-namespace Avalonia.Gtk3
-{
-    class WindowImpl : WindowBaseImpl, IWindowImpl
-    {
-        private WindowState _lastWindowState;
-
-        public WindowImpl() : base(Native.GtkWindowNew(GtkWindowType.TopLevel))
-        {
-        }
-
-        protected unsafe override bool OnStateChanged(IntPtr w, IntPtr pev, IntPtr userData)
-        {
-            var windowStateEvent = (GdkEventWindowState*)pev;
-            var newWindowState = windowStateEvent->new_window_state;
-            var windowState = newWindowState.HasFlag(GdkWindowState.Iconified) ? WindowState.Minimized
-                : (newWindowState.HasFlag(GdkWindowState.Maximized) ? WindowState.Maximized : WindowState.Normal);
-
-            if (windowState != _lastWindowState)
-            {
-                _lastWindowState = windowState;
-                WindowStateChanged?.Invoke(windowState);
-            }
-
-            return base.OnStateChanged(w, pev, userData);
-        }
-
-        public void SetTitle(string title)
-        {
-            using (var t = new Utf8Buffer(title))
-                Native.GtkWindowSetTitle(GtkWidget, t);
-        }
-
-        public WindowState WindowState
-        {
-            get
-            {
-                var state = Native.GdkWindowGetState(Native.GtkWidgetGetWindow(GtkWidget));
-                if (state.HasFlag(GdkWindowState.Iconified))
-                    return WindowState.Minimized;
-                if (state.HasFlag(GdkWindowState.Maximized))
-                    return WindowState.Maximized;
-                return WindowState.Normal;
-            }
-            set
-            {
-                if (value == WindowState.Minimized)
-                    Native.GtkWindowIconify(GtkWidget);
-                else if (value == WindowState.Maximized)
-                    Native.GtkWindowMaximize(GtkWidget);
-                else
-                {
-                    Native.GtkWindowUnmaximize(GtkWidget);
-                    Native.GtkWindowDeiconify(GtkWidget);
-                }
-            }
-        }
-
-        public Action<WindowState> WindowStateChanged { get; set; }
-
-        public void ShowDialog(IWindowImpl parent)
-        {
-            Native.GtkWindowSetModal(GtkWidget, true);
-            Native.GtkWindowSetTransientFor(GtkWidget, ((WindowImpl)parent).GtkWidget.DangerousGetHandle());
-            Native.GtkWindowPresent(GtkWidget);
-        }
-
-        public override void Show()
-        {
-            Native.GtkWindowSetModal(GtkWidget, false);
-            Native.GtkWindowSetTransientFor(GtkWidget, IntPtr.Zero);
-            Native.GtkWindowPresent(GtkWidget);
-        }
-
-        public void SetSystemDecorations(bool enabled) => Native.GtkWindowSetDecorated(GtkWidget, enabled);
-
-        public void SetIcon(IWindowIconImpl icon) => Native.GtkWindowSetIcon(GtkWidget, (Pixbuf) icon);
-
-        public void SetCoverTaskbarWhenMaximized(bool enable)
-        {
-            //Why do we even have that?
-        }
-
-        public void ShowTaskbarIcon(bool value) => Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value);
-
-        public void CanResize(bool value) => Native.GtkWindowSetResizable(GtkWidget, value);
-
-
-        class EmptyDisposable : IDisposable
-        {
-            public void Dispose()
-            {
-
-            }
-        }
-    }
-}

+ 0 - 78
src/Gtk/Avalonia.Gtk3/X11.cs

@@ -1,78 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Avalonia.Gtk3
-{
-    class X11
-    {
-        [DllImport("libX11.so.6")]
-        public static extern IntPtr XInitThreads();
-        
-        [DllImport("libX11.so.6")]
-        public static extern IntPtr XOpenDisplay(IntPtr name);
-        
-        [DllImport("libX11.so.6")]
-        public static extern IntPtr XLockDisplay(IntPtr display);
-        
-        [DllImport("libX11.so.6")]
-        public static extern IntPtr XUnlockDisplay(IntPtr display);
-        
-        [DllImport("libX11.so.6")]
-        public static extern IntPtr XFreeGC(IntPtr display, IntPtr gc);
-        
-        [DllImport("libX11.so.6")]
-        public static extern IntPtr XCreateGC(IntPtr display, IntPtr drawable, ulong valuemask, IntPtr values);
-        
-        [DllImport("libX11.so.6")]
-        public static extern int XInitImage(ref XImage image);
-        
-        [DllImport("libX11.so.6")]
-        public static extern int XDestroyImage(ref XImage image);
-        
-        [DllImport("libX11.so.6")]
-        public static extern IntPtr XSetErrorHandler(XErrorHandler handler);
-
-        [DllImport("libX11.so.6")]
-        public static extern int XSync(IntPtr display, bool discard);
-
-        public delegate int XErrorHandler(IntPtr display, ref XErrorEvent error);
-
-        [DllImport("libX11.so.6")]
-        public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, ref XImage image,
-            int srcx, int srcy, int destx, int desty, uint width, uint height);
-
-        [StructLayout(LayoutKind.Sequential)]
-        public unsafe struct XErrorEvent
-        {
-            public int type;
-            public IntPtr* display; /* Display the event was read from */
-            public ulong serial; /* serial number of failed request */
-            public byte error_code; /* error code of failed request */
-            public byte request_code; /* Major op-code of failed request */
-            public byte minor_code; /* Minor op-code of failed request */
-            public IntPtr resourceid; /* resource id */
-        }
-
-        [StructLayout(LayoutKind.Sequential)]
-        public unsafe struct XImage
-        {
-            public int width, height; /* size of image */
-            public int xoffset; /* number of pixels offset in X direction */
-            public int format; /* XYBitmap, XYPixmap, ZPixmap */
-            public IntPtr data; /* pointer to image data */
-            public int byte_order; /* data byte order, LSBFirst, MSBFirst */
-            public int bitmap_unit; /* quant. of scanline 8, 16, 32 */
-            public int bitmap_bit_order; /* LSBFirst, MSBFirst */
-            public int bitmap_pad; /* 8, 16, 32 either XY or ZPixmap */
-            public int depth; /* depth of image */
-            public int bytes_per_line; /* accelerator to next scanline */
-            public int bits_per_pixel; /* bits per pixel (ZPixmap) */
-            public ulong red_mask; /* bits in z arrangement */
-            public ulong green_mask;
-            public ulong blue_mask;
-            private fixed byte funcs[128];
-        }
-        
-        
-    }
-}

+ 0 - 55
src/Gtk/Avalonia.Gtk3/X11Framebuffer.cs

@@ -1,55 +0,0 @@
-using System;
-using Avalonia.Platform;
-
-namespace Avalonia.Gtk3
-{
-    class X11Framebuffer : ILockedFramebuffer
-    {
-        private readonly IntPtr _display;
-        private readonly IntPtr _xid;
-        private IUnmanagedBlob _blob;
-
-        public X11Framebuffer(IntPtr display, IntPtr xid, int width, int height, int factor)
-        {
-            _display = display;
-            _xid = xid;
-            Size = new PixelSize(width * factor, height * factor);
-            RowBytes = Size.Width * 4;
-            Dpi = new Vector(96, 96) * factor;
-            Format = PixelFormat.Bgra8888;
-            _blob = AvaloniaLocator.Current.GetService<IRuntimePlatform>().AllocBlob(RowBytes * Size.Height);
-            Address = _blob.Address;
-        }
-        
-        public void Dispose()
-        {
-            var image = new X11.XImage();
-            int bitsPerPixel = 32;
-            image.width = Size.Width;
-            image.height = Size.Height;
-            image.format = 2; //ZPixmap;
-            image.data = Address;
-            image.byte_order = 0;// LSBFirst;
-            image.bitmap_unit = bitsPerPixel;
-            image.bitmap_bit_order = 0;// LSBFirst;
-            image.bitmap_pad = bitsPerPixel;
-            image.depth = 24;
-            image.bytes_per_line = RowBytes - Size.Width * 4;
-            image.bits_per_pixel = bitsPerPixel;
-            X11.XLockDisplay(_display);
-            X11.XInitImage(ref image);
-            var gc = X11.XCreateGC(_display, _xid, 0, IntPtr.Zero);
-            X11.XPutImage(_display, _xid, gc, ref image, 0, 0, 0, 0, (uint)Size.Width, (uint)Size.Height);
-            X11.XFreeGC(_display, gc);
-            X11.XSync(_display, true);
-            X11.XUnlockDisplay(_display);
-            _blob.Dispose();
-        }
-
-        public IntPtr Address { get; }
-        public PixelSize Size { get; }
-        public int RowBytes { get; }
-        public Vector Dpi { get; }
-        public PixelFormat Format { get; }
-    }
-}

+ 1 - 1
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@@ -18,7 +18,7 @@ namespace Avalonia.LinuxFramebuffer
         {
             _fb = fb;
             Invalidate(default(Rect));
-            var mice = new Mice(ClientSize.Width, ClientSize.Height);
+            var mice = new Mice(this, ClientSize.Width, ClientSize.Height);
             mice.Start();
             mice.Event += e => Input?.Invoke(e);
         }

+ 55 - 19
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@@ -2,6 +2,7 @@
 using System.Diagnostics;
 using System.Threading;
 using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Controls.Embedding;
 using Avalonia.Controls.Platform;
 using Avalonia.Input;
@@ -21,7 +22,6 @@ namespace Avalonia.LinuxFramebuffer
         private static readonly Stopwatch St = Stopwatch.StartNew();
         internal static uint Timestamp => (uint)St.ElapsedTicks;
         public static InternalPlatformThreadingInterface Threading;
-        public static FramebufferToplevelImpl TopLevel;
         LinuxFramebufferPlatform(string fbdev = null)
         {
             _fb = new LinuxFramebuffer(fbdev);
@@ -41,37 +41,73 @@ namespace Avalonia.LinuxFramebuffer
                 .Bind<IRenderTimer>().ToConstant(Threading);
         }
 
-        internal static TopLevel Initialize<T>(T builder, string fbdev = null) where T : AppBuilderBase<T>, new()
+        internal static LinuxFramebufferLifetime Initialize<T>(T builder, string fbdev = null) where T : AppBuilderBase<T>, new()
         {
             var platform = new LinuxFramebufferPlatform(fbdev);
-            builder.UseSkia().UseWindowingSubsystem(platform.Initialize, "fbdev")
-                .SetupWithoutStarting();
-            var tl = new EmbeddableControlRoot(TopLevel = new FramebufferToplevelImpl(platform._fb));
-            tl.Prepare();
-            return tl;
+            builder.UseSkia().UseWindowingSubsystem(platform.Initialize, "fbdev");
+            return new LinuxFramebufferLifetime(platform._fb);
         }
     }
-}
 
-public static class LinuxFramebufferPlatformExtensions
-{
-    class TokenClosable : ICloseable
+    class LinuxFramebufferLifetime : IControlledApplicationLifetime, ISingleViewApplicationLifetime
     {
-        public event EventHandler Closed;
+        private readonly LinuxFramebuffer _fb;
+        private TopLevel _topLevel;
+        private readonly CancellationTokenSource _cts = new CancellationTokenSource();
+        public CancellationToken Token => _cts.Token;
 
-        public TokenClosable(CancellationToken token)
+        public LinuxFramebufferLifetime(LinuxFramebuffer fb)
+        {
+            _fb = fb;
+        }
+        
+        public Control MainView
         {
-            token.Register(() => Dispatcher.UIThread.Post(() => Closed?.Invoke(this, new EventArgs())));
+            get => (Control)_topLevel?.Content;
+            set
+            {
+                if (_topLevel == null)
+                {
+
+                    var tl = new EmbeddableControlRoot(new FramebufferToplevelImpl(_fb));
+                    tl.Prepare();
+                    _topLevel = tl;
+                }
+                _topLevel.Content = value;
+            }
+        }
+
+        public int ExitCode { get; private set; }
+        public event EventHandler<ControlledApplicationLifetimeStartupEventArgs> Startup;
+        public event EventHandler<ControlledApplicationLifetimeExitEventArgs> Exit;
+
+        public void Start(string[] args)
+        {
+            Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args));
+        }
+        
+        public void Shutdown(int exitCode)
+        {
+            ExitCode = exitCode;
+            var e = new ControlledApplicationLifetimeExitEventArgs(exitCode);
+            Exit?.Invoke(this, e);
+            ExitCode = e.ApplicationExitCode;
+            _cts.Cancel();
         }
     }
+}
 
-    public static void InitializeWithLinuxFramebuffer<T>(this T builder, Action<TopLevel> setup,
-        CancellationToken stop = default(CancellationToken), string fbdev = null)
+public static class LinuxFramebufferPlatformExtensions
+{
+    public static int StartLinuxFramebuffer<T>(this T builder, string[] args, string fbdev = null)
         where T : AppBuilderBase<T>, new()
     {
-        setup(LinuxFramebufferPlatform.Initialize(builder, fbdev));
-        builder.BeforeStartCallback(builder);
-        builder.Instance.Run(new TokenClosable(stop));
+        var lifetime = LinuxFramebufferPlatform.Initialize(builder, fbdev);
+        builder.Instance.ApplicationLifetime = lifetime;
+        builder.SetupWithoutStarting();
+        lifetime.Start(args);
+        builder.Instance.Run(lifetime.Token);
+        return lifetime.ExitCode;
     }
 }
 

+ 7 - 5
src/Linux/Avalonia.LinuxFramebuffer/Mice.cs

@@ -7,8 +7,9 @@ using Avalonia.Platform;
 
 namespace Avalonia.LinuxFramebuffer
 {
-    public unsafe class Mice
+    unsafe class Mice
     {
+        private readonly FramebufferToplevelImpl _topLevel;
         private readonly double _width;
         private readonly double _height;
         private double _x;
@@ -16,8 +17,9 @@ namespace Avalonia.LinuxFramebuffer
 
         public event Action<RawInputEventArgs> Event;
 
-        public Mice(double width, double height)
+        public Mice(FramebufferToplevelImpl topLevel, double width, double height)
         {
+            _topLevel = topLevel;
             _width = width;
             _height = height;
         }
@@ -78,7 +80,7 @@ namespace Avalonia.LinuxFramebuffer
                     return;
                 Event?.Invoke(new RawPointerEventArgs(LinuxFramebufferPlatform.MouseDevice,
                     LinuxFramebufferPlatform.Timestamp,
-                    LinuxFramebufferPlatform.TopLevel.InputRoot, RawPointerEventType.Move, new Point(_x, _y),
+                    _topLevel.InputRoot, RawPointerEventType.Move, new Point(_x, _y),
                     InputModifiers.None));
             }
             if (ev.type ==(int) EvType.EV_ABS)
@@ -91,7 +93,7 @@ namespace Avalonia.LinuxFramebuffer
                     return;
                 Event?.Invoke(new RawPointerEventArgs(LinuxFramebufferPlatform.MouseDevice,
                     LinuxFramebufferPlatform.Timestamp,
-                    LinuxFramebufferPlatform.TopLevel.InputRoot, RawPointerEventType.Move, new Point(_x, _y),
+                    _topLevel.InputRoot, RawPointerEventType.Move, new Point(_x, _y),
                     InputModifiers.None));
             }
             if (ev.type == (short) EvType.EV_KEY)
@@ -108,7 +110,7 @@ namespace Avalonia.LinuxFramebuffer
 
                 Event?.Invoke(new RawPointerEventArgs(LinuxFramebufferPlatform.MouseDevice,
                     LinuxFramebufferPlatform.Timestamp,
-                    LinuxFramebufferPlatform.TopLevel.InputRoot, type.Value, new Point(_x, _y), default(InputModifiers)));
+                    _topLevel.InputRoot, type.Value, new Point(_x, _y), default(InputModifiers)));
             }
         }
     }

+ 12 - 16
src/Markup/Avalonia.Markup/Data/MultiBinding.cs

@@ -64,14 +64,19 @@ namespace Avalonia.Data
             object anchor = null,
             bool enableDataValidation = false)
         {
-            if (Converter == null)
+            var targetType = targetProperty?.PropertyType ?? typeof(object);
+            var converter = Converter;
+            // We only respect `StringFormat` if the type of the property we're assigning to will
+            // accept a string. Note that this is slightly different to WPF in that WPF only applies
+            // `StringFormat` for target type `string` (not `object`).
+            if (!string.IsNullOrWhiteSpace(StringFormat) && 
+                (targetType == typeof(string) || targetType == typeof(object)))
             {
-                throw new NotSupportedException("MultiBinding without Converter not currently supported.");
+                converter = new StringFormatMultiValueConverter(StringFormat, converter);
             }
-
-            var targetType = targetProperty?.PropertyType ?? typeof(object);
+            
             var children = Bindings.Select(x => x.Initiate(target, null));
-            var input = children.Select(x => x.Observable).CombineLatest().Select(x => ConvertValue(x, targetType));
+            var input = children.Select(x => x.Observable).CombineLatest().Select(x => ConvertValue(x, targetType, converter));
             var mode = Mode == BindingMode.Default ?
                 targetProperty?.GetMetadata(target.GetType()).DefaultBindingMode : Mode;
 
@@ -87,25 +92,16 @@ namespace Avalonia.Data
             }
         }
 
-        private object ConvertValue(IList<object> values, Type targetType)
+        private object ConvertValue(IList<object> values, Type targetType, IMultiValueConverter converter)
         {
             var culture = CultureInfo.CurrentCulture;
-            var converted = Converter.Convert(values, targetType, ConverterParameter, culture);
+            var converted = converter.Convert(values, targetType, ConverterParameter, culture);
 
             if (converted == AvaloniaProperty.UnsetValue && FallbackValue != null)
             {
                 converted = FallbackValue;
             }
 
-            // We only respect `StringFormat` if the type of the property we're assigning to will
-            // accept a string. Note that this is slightly different to WPF in that WPF only applies
-            // `StringFormat` for target type `string` (not `object`).
-            if (!string.IsNullOrWhiteSpace(StringFormat) && 
-                (targetType == typeof(string) || targetType == typeof(object)))
-            {
-                converted = string.Format(culture, StringFormat, converted);
-            }
-
             return converted;
         }
     }

+ 0 - 120
tests/Avalonia.Controls.UnitTests/ApplicationTests.cs

@@ -11,113 +11,6 @@ namespace Avalonia.Controls.UnitTests
 {
     public class ApplicationTests
     {
-        [Fact]
-        public void Should_Exit_After_MainWindow_Closed()
-        {
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
-
-                var hasExit = false;
-
-                Application.Current.Exit += (s, e) => hasExit = true;
-
-                var mainWindow = new Window();
-
-                mainWindow.Show();
-
-                Application.Current.MainWindow = mainWindow;
-
-                var window = new Window();
-
-                window.Show();
-
-                mainWindow.Close();
-
-                Assert.True(hasExit);
-            }
-        }
-
-        [Fact]
-        public void Should_Exit_After_Last_Window_Closed()
-        {
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                Application.Current.ShutdownMode = ShutdownMode.OnLastWindowClose;
-
-                var hasExit = false;
-
-                Application.Current.Exit += (s, e) => hasExit = true;
-
-                var windowA = new Window();
-
-                windowA.Show();
-
-                var windowB = new Window();
-
-                windowB.Show();
-
-                windowA.Close();
-
-                Assert.False(hasExit);
-
-                windowB.Close();
-
-                Assert.True(hasExit);
-            }
-        }
-
-        [Fact]
-        public void Should_Only_Exit_On_Explicit_Exit()
-        {
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
-
-                var hasExit = false;
-
-                Application.Current.Exit += (s, e) => hasExit = true;
-
-                var windowA = new Window();
-
-                windowA.Show();
-
-                var windowB = new Window();
-
-                windowB.Show();
-
-                windowA.Close();
-
-                Assert.False(hasExit);
-
-                windowB.Close();
-
-                Assert.False(hasExit);
-
-                Application.Current.Shutdown();
-
-                Assert.True(hasExit);
-            }
-        }
-
-        [Fact]
-        public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call()
-        {
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                var windows = new List<Window> { new Window(), new Window(), new Window(), new Window() };
-
-                foreach (var window in windows)
-                {
-                    window.Show();
-                }
-
-                Application.Current.Shutdown();
-
-                Assert.Empty(Application.Current.Windows);
-            }
-        }
-
         [Fact]
         public void Throws_ArgumentNullException_On_Run_If_MainWindow_Is_Null()
         {
@@ -142,18 +35,5 @@ namespace Avalonia.Controls.UnitTests
                 Assert.True(raised);
             }
         }
-
-        [Fact]
-        public void Should_Set_ExitCode_After_Shutdown()
-        {
-            using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
-            {
-                Application.Current.Shutdown(1337);
-
-                var exitCode = Application.Current.Run();
-
-                Assert.Equal(1337, exitCode);
-            }
-        }
     }
 }

+ 1 - 3
tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

@@ -59,9 +59,7 @@ namespace Avalonia.Controls.UnitTests
                 };
 
                 var window = new Window { Content = target };
-
-                Avalonia.Application.Current.MainWindow = window;
-
+                
                 _mouse.Click(target, MouseButton.Right);
 
                 Assert.True(sut.IsOpen);

+ 213 - 0
tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs

@@ -0,0 +1,213 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Platform;
+using Avalonia.Threading;
+using Avalonia.UnitTests;
+using Moq;
+using Xunit;
+
+namespace Avalonia.Controls.UnitTests
+{
+    
+    public class DesktopStyleApplicationLifetimeTests
+    {
+        [Fact]
+        public void Should_Set_ExitCode_After_Shutdown()
+        {
+            using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
+            using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current))    
+            {
+                lifetime.Shutdown(1337);
+
+                var exitCode = lifetime.Start(Array.Empty<string>());
+
+                Assert.Equal(1337, exitCode);
+            }
+        }
+        
+        
+        [Fact]
+        public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current))
+            {
+                var windows = new List<Window> { new Window(), new Window(), new Window(), new Window() };
+
+                foreach (var window in windows)
+                {
+                    window.Show();
+                }
+                Assert.Equal(4, lifetime.Windows.Count);
+                lifetime.Shutdown();
+
+                Assert.Empty(lifetime.Windows);
+            }
+        }
+        
+        [Fact]
+        public void Should_Only_Exit_On_Explicit_Exit()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current))
+            {
+                lifetime.ShutdownMode = ShutdownMode.OnExplicitShutdown;
+
+                var hasExit = false;
+
+                lifetime.Exit += (s, e) => hasExit = true;
+
+                var windowA = new Window();
+
+                windowA.Show();
+
+                var windowB = new Window();
+
+                windowB.Show();
+
+                windowA.Close();
+
+                Assert.False(hasExit);
+
+                windowB.Close();
+
+                Assert.False(hasExit);
+
+                lifetime.Shutdown();
+
+                Assert.True(hasExit);
+            }
+        }
+        
+        [Fact]
+        public void Should_Exit_After_MainWindow_Closed()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current))
+            {
+                lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose;
+
+                var hasExit = false;
+
+                lifetime.Exit += (s, e) => hasExit = true;
+
+                var mainWindow = new Window();
+
+                mainWindow.Show();
+
+                lifetime.MainWindow = mainWindow;
+
+                var window = new Window();
+
+                window.Show();
+
+                mainWindow.Close();
+
+                Assert.True(hasExit);
+            }
+        }
+
+        [Fact]
+        public void Should_Exit_After_Last_Window_Closed()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current))
+            {
+                lifetime.ShutdownMode = ShutdownMode.OnLastWindowClose;
+
+                var hasExit = false;
+
+                lifetime.Exit += (s, e) => hasExit = true;
+
+                var windowA = new Window();
+
+                windowA.Show();
+
+                var windowB = new Window();
+
+                windowB.Show();
+
+                windowA.Close();
+
+                Assert.False(hasExit);
+
+                windowB.Close();
+
+                Assert.True(hasExit);
+            }
+        }
+        
+        [Fact]
+        public void Show_Should_Add_Window_To_OpenWindows()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current))
+            {
+                var window = new Window();
+
+                window.Show();
+
+                Assert.Equal(new[] { window }, lifetime.Windows);
+            }
+        }
+
+        [Fact]
+        public void Window_Should_Be_Added_To_OpenWindows_Only_Once()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current))
+            {
+                var window = new Window();
+
+                window.Show();
+                window.Show();
+                window.IsVisible = true;
+
+                Assert.Equal(new[] { window }, lifetime.Windows);
+
+                window.Close();
+            }
+        }
+
+        [Fact]
+        public void Close_Should_Remove_Window_From_OpenWindows()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current))
+            {
+                var window = new Window();
+
+                window.Show();
+                Assert.Equal(1, lifetime.Windows.Count);
+                window.Close();
+
+                Assert.Empty(lifetime.Windows);
+            }
+        }
+        
+        [Fact]
+        public void Impl_Closing_Should_Remove_Window_From_OpenWindows()
+        {
+            var windowImpl = new Mock<IWindowImpl>();
+            windowImpl.SetupProperty(x => x.Closed);
+            windowImpl.Setup(x => x.Scaling).Returns(1);
+
+            var services = TestServices.StyledWindow.With(
+                windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object));
+
+            using (UnitTestApplication.Start(services))
+            using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current))
+            {
+                var window = new Window();
+
+                window.Show();
+                Assert.Equal(1, lifetime.Windows.Count);
+                windowImpl.Object.Closed();
+
+                Assert.Empty(lifetime.Windows);
+            }
+        }
+    }
+    
+}

+ 38 - 1
tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs

@@ -8,6 +8,7 @@ using Avalonia.Controls.Templates;
 using Avalonia.Data;
 using Avalonia.Markup.Data;
 using Avalonia.Styling;
+using Avalonia.UnitTests;
 using Xunit;
 
 namespace Avalonia.Controls.UnitTests.Primitives
@@ -22,6 +23,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 Minimum = 100,
                 Maximum = 50,
             };
+            var root = new TestRoot(target);
 
             Assert.Equal(100, target.Minimum);
             Assert.Equal(100, target.Maximum);
@@ -36,6 +38,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 Maximum = 50,
                 Value = 100,
             };
+            var root = new TestRoot(target);
 
             Assert.Equal(0, target.Minimum);
             Assert.Equal(50, target.Maximum);
@@ -51,6 +54,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 Maximum = 100,
                 Value = 50,
             };
+            var root = new TestRoot(target);
 
             target.Minimum = 200;
 
@@ -68,6 +72,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 Maximum = 100,
                 Value = 100,
             };
+            var root = new TestRoot(target);
 
             target.Maximum = 50;
 
@@ -160,6 +165,38 @@ namespace Avalonia.Controls.UnitTests.Primitives
             Assert.Equal(expected, track.Value);
         }
 
+        [Fact]
+        public void Coercion_Should_Not_Be_Done_During_Initialization()
+        {
+            var target = new TestRange();
+
+            target.BeginInit();
+
+            var root = new TestRoot(target);
+            target.Minimum = 1;
+            Assert.Equal(0, target.Value);
+
+            target.Value = 50;
+            target.EndInit();
+
+            Assert.Equal(50, target.Value);
+        }
+
+        [Fact]
+        public void Coercion_Should_Be_Done_After_Initialization()
+        {
+            var target = new TestRange();
+
+            target.BeginInit();
+
+            var root = new TestRoot(target);
+            target.Minimum = 1;
+
+            target.EndInit();
+
+            Assert.Equal(1, target.Value);
+        }
+
         private class TestRange : RangeBase
         {
         }
@@ -199,4 +236,4 @@ namespace Avalonia.Controls.UnitTests.Primitives
             }
         }
     }
-}
+}

+ 113 - 2
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@@ -536,6 +536,9 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 SelectedIndex = 1,
             };
 
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
             var called = false;
 
             target.SelectionChanged += (s, e) =>
@@ -545,8 +548,6 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 called = true;
             };
 
-            target.ApplyTemplate();
-            target.Presenter.ApplyTemplate();
             target.SelectedIndex = -1;
 
             Assert.True(called);
@@ -783,6 +784,116 @@ namespace Avalonia.Controls.UnitTests.Primitives
             Assert.Equal(2, vm.Child.SelectedIndex);
         }
 
+        [Fact]
+        public void Should_Select_Correct_Item_When_Duplicate_Items_Are_Present()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz"},
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            _helper.Down((Interactive)target.Presenter.Panel.Children[3]);
+
+            Assert.Equal(3, target.SelectedIndex);
+        }
+
+        [Fact]
+        public void Should_Apply_Selected_Pseudoclass_To_Correct_Item_When_Duplicate_Items_Are_Present()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            _helper.Down((Interactive)target.Presenter.Panel.Children[3]);
+
+            Assert.Equal(new[] { ":selected" }, target.Presenter.Panel.Children[3].Classes);
+        }
+
+        [Fact]
+        public void Adding_Item_Before_SelectedItem_Should_Update_SelectedIndex()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "Baz"
+            };
+
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = items,
+                SelectedIndex = 1,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            items.Insert(0, "Qux");
+
+            Assert.Equal(2, target.SelectedIndex);
+            Assert.Equal("Bar", target.SelectedItem);
+        }
+
+        [Fact]
+        public void Removing_Item_Before_SelectedItem_Should_Update_SelectedIndex()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "Baz"
+            };
+
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = items,
+                SelectedIndex = 1,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            items.RemoveAt(0);
+
+            Assert.Equal(0, target.SelectedIndex);
+            Assert.Equal("Bar", target.SelectedItem);
+        }
+
+        [Fact]
+        public void Replacing_Selected_Item_Should_Update_SelectedItem()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "Baz"
+            };
+
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = items,
+                SelectedIndex = 1,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            items[1] = "Qux";
+
+            Assert.Equal(1, target.SelectedIndex);
+            Assert.Equal("Qux", target.SelectedItem);
+        }
+
         private FuncControlTemplate Template()
         {
             return new FuncControlTemplate<SelectingItemsControl>(control =>

+ 493 - 30
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@@ -4,12 +4,15 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Linq;
 using Avalonia.Collections;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
 using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Interactivity;
 using Avalonia.Markup.Data;
 using Xunit;
 
@@ -17,6 +20,8 @@ namespace Avalonia.Controls.UnitTests.Primitives
 {
     public class SelectingItemsControlTests_Multiple
     {
+        private MouseTestHelper _helper = new MouseTestHelper();
+
         [Fact]
         public void Setting_SelectedIndex_Should_Add_To_SelectedItems()
         {
@@ -258,31 +263,25 @@ namespace Avalonia.Controls.UnitTests.Primitives
         }
 
         [Fact]
-        public void Replacing_First_SelectedItem_Should_Update_SelectedItem_SelectedIndex()
+        public void Setting_SelectedIndex_Should_Unmark_Previously_Selected_Containers()
         {
-            var items = new[]
-            {
-                new ListBoxItem(),
-                new ListBoxItem(),
-                new ListBoxItem(),
-            };
-
             var target = new TestSelector
             {
-                Items = items,
+                Items = new[] { "foo", "bar", "baz" },
                 Template = Template(),
             };
 
             target.ApplyTemplate();
             target.Presenter.ApplyTemplate();
-            target.SelectedIndex = 1;
-            target.SelectedItems[0] = items[2];
 
-            Assert.Equal(2, target.SelectedIndex);
-            Assert.Equal(items[2], target.SelectedItem);
-            Assert.False(items[0].IsSelected);
-            Assert.False(items[1].IsSelected);
-            Assert.True(items[2].IsSelected);
+            target.SelectedItems.Add("foo");
+            target.SelectedItems.Add("bar");
+
+            Assert.Equal(new[] { 0, 1 }, SelectedContainers(target));
+
+            target.SelectedIndex = 2;
+
+            Assert.Equal(new[] { 2 }, SelectedContainers(target));
         }
 
         [Fact]
@@ -361,6 +360,52 @@ namespace Avalonia.Controls.UnitTests.Primitives
             Assert.Equal(new[] { "baz", "qux", "qiz" }, target.SelectedItems.Cast<object>().ToList());
         }
 
+        [Fact]
+        public void Setting_SelectedIndex_After_Range_Should_Unmark_Previously_Selected_Containers()
+        {
+            var target = new TestSelector
+            {
+                Items = new[] { "foo", "bar", "baz", "qux" },
+                Template = Template(),
+                SelectedIndex = 0,
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            target.SelectRange(2);
+
+            Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target));
+
+            target.SelectedIndex = 3;
+
+            Assert.Equal(new[] { 3 }, SelectedContainers(target));
+        }
+
+        [Fact]
+        public void Toggling_Selection_After_Range_Should_Work()
+        {
+            var target = new TestSelector
+            {
+                Items = new[] { "foo", "bar", "baz", "foo", "bar", "baz" },
+                Template = Template(),
+                SelectedIndex = 0,
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            target.SelectRange(3);
+
+            Assert.Equal(new[] { 0, 1, 2, 3 }, SelectedContainers(target));
+
+            target.Toggle(4);
+
+            Assert.Equal(new[] { 0, 1, 2, 3, 4 }, SelectedContainers(target));
+        }
+
         [Fact]
         public void Suprious_SelectedIndex_Changes_Should_Not_Be_Triggered()
         {
@@ -382,6 +427,40 @@ namespace Avalonia.Controls.UnitTests.Primitives
             Assert.Equal(new[] { -1, 1, 0 }, selectedIndexes);
         }
 
+        [Fact]
+        public void Can_Set_SelectedIndex_To_Another_Selected_Item()
+        {
+            var target = new TestSelector
+            {
+                Items = new[] { "foo", "bar", "baz" },
+                Template = Template(),
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            target.SelectedItems.Add("foo");
+            target.SelectedItems.Add("bar");
+
+            Assert.Equal(0, target.SelectedIndex);
+            Assert.Equal(new[] { "foo", "bar" }, target.SelectedItems);
+            Assert.Equal(new[] { 0, 1 }, SelectedContainers(target));
+
+            var raised = false;
+            target.SelectionChanged += (s, e) =>
+            {
+                raised = true;
+                Assert.Empty(e.AddedItems);
+                Assert.Equal(new[] { "foo" }, e.RemovedItems);
+            };
+
+            target.SelectedIndex = 1;
+
+            Assert.True(raised);
+            Assert.Equal(1, target.SelectedIndex);
+            Assert.Equal(new[] { "bar" }, target.SelectedItems);
+            Assert.Equal(new[] { 1 }, SelectedContainers(target));
+        }
+
         /// <summary>
         /// Tests a problem discovered with ListBox with selection.
         /// </summary>
@@ -471,6 +550,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
             {
                 DataContext = items,
                 Template = Template(),
+                Items = items,
             };
 
             var called = false;
@@ -540,35 +620,418 @@ namespace Avalonia.Controls.UnitTests.Primitives
 
             Assert.True(called);
         }
+        
+        [Fact]
+        public void Shift_Selecting_From_No_Selection_Selects_From_Start()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz" },
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            _helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Shift);
+
+            var panel = target.Presenter.Panel;
+
+            Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems);
+            Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target));
+        }
 
         [Fact]
-        public void Replacing_SelectedItems_Should_Raise_SelectionChanged_With_CorrectItems()
+        public void Ctrl_Selecting_SelectedItem_With_Multiple_Selection_Active_Sets_SelectedItem_To_Next_Selection()
         {
-            var items = new[] { "foo", "bar", "baz" };
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz", "Qux" },
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            _helper.Click((Interactive)target.Presenter.Panel.Children[1]);
+            _helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Control);
+            _helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: InputModifiers.Control);
+
+            Assert.Equal(1, target.SelectedIndex);
+            Assert.Equal("Bar", target.SelectedItem);
+            Assert.Equal(new[] { "Bar", "Baz", "Qux" }, target.SelectedItems);
+
+            _helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: InputModifiers.Control);
+
+            Assert.Equal(2, target.SelectedIndex);
+            Assert.Equal("Baz", target.SelectedItem);
+            Assert.Equal(new[] { "Baz", "Qux" }, target.SelectedItems);
+        }
+
+        [Fact]
+        public void Ctrl_Selecting_Non_SelectedItem_With_Multiple_Selection_Active_Leaves_SelectedItem_The_Same()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz" },
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            _helper.Click((Interactive)target.Presenter.Panel.Children[1]);
+            _helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Control);
+
+            Assert.Equal(1, target.SelectedIndex);
+            Assert.Equal("Bar", target.SelectedItem);
+
+            _helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Control);
+
+            Assert.Equal(1, target.SelectedIndex);
+            Assert.Equal("Bar", target.SelectedItem);
+        }
+
+        [Fact]
+        public void Should_Ctrl_Select_Correct_Item_When_Duplicate_Items_Are_Present()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            _helper.Click((Interactive)target.Presenter.Panel.Children[3]);
+            _helper.Click((Interactive)target.Presenter.Panel.Children[4], modifiers: InputModifiers.Control);
+
+            var panel = target.Presenter.Panel;
+
+            Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems);
+            Assert.Equal(new[] { 3, 4 }, SelectedContainers(target));
+        }
+
+        [Fact]
+        public void Should_Shift_Select_Correct_Item_When_Duplicates_Are_Present()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            _helper.Click((Interactive)target.Presenter.Panel.Children[3]);
+            _helper.Click((Interactive)target.Presenter.Panel.Children[5], modifiers: InputModifiers.Shift);
+
+            var panel = target.Presenter.Panel;
 
+            Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems);
+            Assert.Equal(new[] { 3, 4, 5 }, SelectedContainers(target));
+        }
+
+        [Fact]
+        public void Can_Shift_Select_All_Items_When_Duplicates_Are_Present()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            _helper.Click((Interactive)target.Presenter.Panel.Children[0]);
+            _helper.Click((Interactive)target.Presenter.Panel.Children[5], modifiers: InputModifiers.Shift);
+
+            var panel = target.Presenter.Panel;
+
+            Assert.Equal(new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, target.SelectedItems);
+            Assert.Equal(new[] { 0, 1, 2, 3, 4, 5 }, SelectedContainers(target));
+        }
+
+        [Fact]
+        public void Duplicate_Items_Are_Added_To_SelectedItems_In_Order()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            _helper.Click((Interactive)target.Presenter.Panel.Children[0]);
+
+            Assert.Equal(new[] { "Foo" }, target.SelectedItems);
+
+            _helper.Click((Interactive)target.Presenter.Panel.Children[4], modifiers: InputModifiers.Control);
+
+            Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems);
+
+            _helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: InputModifiers.Control);
+
+            Assert.Equal(new[] { "Foo", "Bar", "Foo" }, target.SelectedItems);
+
+            _helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: InputModifiers.Control);
+
+            Assert.Equal(new[] { "Foo", "Bar", "Foo", "Bar" }, target.SelectedItems);
+        }
+
+        [Fact]
+        public void SelectAll_Sets_SelectedIndex_And_SelectedItem()
+        {
+            var target = new TestSelector
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz" },
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            target.SelectAll();
+
+            Assert.Equal(0, target.SelectedIndex);
+            Assert.Equal("Foo", target.SelectedItem);
+        }
+
+        [Fact]
+        public void UnselectAll_Clears_SelectedIndex_And_SelectedItem()
+        {
+            var target = new TestSelector
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz" },
+                SelectionMode = SelectionMode.Multiple,
+                SelectedIndex = 0,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            target.UnselectAll();
+
+            Assert.Equal(-1, target.SelectedIndex);
+            Assert.Equal(null, target.SelectedItem);
+        }
+
+        [Fact]
+        public void SelectAll_Handles_Duplicate_Items()
+        {
             var target = new TestSelector
             {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            target.SelectAll();
+
+            Assert.Equal(new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, target.SelectedItems);
+        }
+
+        [Fact]
+        public void Adding_Item_Before_SelectedItems_Should_Update_Selection()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "Baz"
+            };
+
+            var target = new ListBox
+            {
+                Template = Template(),
                 Items = items,
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            target.SelectAll();
+            items.Insert(0, "Qux");
+
+            Assert.Equal(1, target.SelectedIndex);
+            Assert.Equal("Foo", target.SelectedItem);
+            Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems);
+            Assert.Equal(new[] { 1, 2, 3 }, SelectedContainers(target));
+        }
+
+        [Fact]
+        public void Removing_Item_Before_SelectedItem_Should_Update_Selection()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "Baz"
+            };
+
+            var target = new TestSelector
+            {
                 Template = Template(),
-                SelectedItem = "bar",
+                Items = items,
+                SelectionMode = SelectionMode.Multiple,
             };
 
-            var called = false;
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
 
-            target.SelectionChanged += (s, e) =>
+            target.SelectedIndex = 1;
+            target.SelectRange(2);
+
+            Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems);
+
+            items.RemoveAt(0);
+
+            Assert.Equal(0, target.SelectedIndex);
+            Assert.Equal("Bar", target.SelectedItem);
+            Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems);
+            Assert.Equal(new[] { 0, 1 }, SelectedContainers(target));
+        }
+
+        [Fact]
+        public void Removing_SelectedItem_With_Multiple_Selection_Active_Should_Update_Selection()
+        {
+            var items = new ObservableCollection<string>
             {
-                Assert.Equal(new[] { "foo",}, e.AddedItems.Cast<object>());
-                Assert.Equal(new[] { "bar" }, e.RemovedItems.Cast<object>());
-                called = true;
+               "Foo",
+               "Bar",
+               "Baz"
+            };
+
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = items,
+                SelectionMode = SelectionMode.Multiple,
             };
 
             target.ApplyTemplate();
             target.Presenter.ApplyTemplate();
-            target.SelectedItems[0] = "foo";
 
-            Assert.True(called);
+            target.SelectAll();
+            items.RemoveAt(0);
+
+            Assert.Equal(0, target.SelectedIndex);
+            Assert.Equal("Bar", target.SelectedItem);
+            Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems);
+            Assert.Equal(new[] { 0, 1 }, SelectedContainers(target));
         }
 
+        [Fact]
+        public void Replacing_Selected_Item_Should_Update_SelectedItems()
+        {
+            var items = new ObservableCollection<string>
+            {
+               "Foo",
+               "Bar",
+               "Baz"
+            };
+
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = items,
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+
+            target.SelectAll();
+            items[1] = "Qux";
+
+            Assert.Equal(new[] { "Foo", "Qux", "Baz" }, target.SelectedItems);
+        }
+
+        [Fact]
+        public void Left_Click_On_SelectedItem_Should_Clear_Existing_Selection()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz" },
+                ItemTemplate = new FuncDataTemplate<string>(x => new TextBlock { Width = 20, Height = 10 }),
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            target.SelectAll();
+
+            Assert.Equal(3, target.SelectedItems.Count);
+
+            _helper.Click((Interactive)target.Presenter.Panel.Children[0]);
+
+            Assert.Equal(1, target.SelectedItems.Count);
+            Assert.Equal(new[] { "Foo", }, target.SelectedItems);
+            Assert.Equal(new[] { 0 }, SelectedContainers(target));
+        }
+
+        [Fact]
+        public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz" },
+                ItemTemplate = new FuncDataTemplate<string>(x => new TextBlock { Width = 20, Height = 10 }),
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            target.SelectAll();
+
+            Assert.Equal(3, target.SelectedItems.Count);
+
+            _helper.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right);
+
+            Assert.Equal(3, target.SelectedItems.Count);
+        }
+
+        [Fact]
+        public void Right_Click_On_UnselectedItem_Should_Clear_Existing_Selection()
+        {
+            var target = new ListBox
+            {
+                Template = Template(),
+                Items = new[] { "Foo", "Bar", "Baz" },
+                ItemTemplate = new FuncDataTemplate<string>(x => new TextBlock { Width = 20, Height = 10 }),
+                SelectionMode = SelectionMode.Multiple,
+            };
+
+            target.ApplyTemplate();
+            target.Presenter.ApplyTemplate();
+            _helper.Click((Interactive)target.Presenter.Panel.Children[0]);
+            _helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: InputModifiers.Shift);
+
+            Assert.Equal(2, target.SelectedItems.Count);
+
+            _helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right);
+
+            Assert.Equal(1, target.SelectedItems.Count);
+        }
+
+        private IEnumerable<int> SelectedContainers(SelectingItemsControl target)
+        {
+            return target.Presenter.Panel.Children
+                .Select((x, i) => x.Classes.Contains(":selected") ? i : -1)
+                .Where(x => x != -1);
+        }
 
         private FuncControlTemplate Template()
         {
@@ -598,10 +1061,10 @@ namespace Avalonia.Controls.UnitTests.Primitives
                 set { base.SelectionMode = value; }
             }
 
-            public void SelectRange(int index)
-            {
-                UpdateSelection(index, true, true);
-            }
+            public new void SelectAll() => base.SelectAll();
+            public new void UnselectAll() => base.UnselectAll();
+            public void SelectRange(int index) => UpdateSelection(index, true, true);
+            public void Toggle(int index) => UpdateSelection(index, true, false, true);
         }
 
         private class OldDataContextViewModel

+ 0 - 19
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@@ -207,19 +207,6 @@ namespace Avalonia.Controls.UnitTests
             }
         }
 
-        [Fact]
-        public void Exiting_Application_Notifies_Top_Level()
-        {
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                var impl = new Mock<ITopLevelImpl>();
-                impl.SetupAllProperties();
-                var target = new TestTopLevel(impl.Object);
-                UnitTestApplication.Current.Shutdown();
-                Assert.True(target.IsClosed);
-            }
-        }
-
         [Fact]
         public void Adding_Resource_To_Application_Should_Raise_ResourcesChanged()
         {
@@ -259,12 +246,6 @@ namespace Avalonia.Controls.UnitTests
             }
 
             protected override ILayoutManager CreateLayoutManager() => _layoutManager;
-
-            protected override void HandleApplicationExiting()
-            {
-                base.HandleApplicationExiting();
-                IsClosed = true;
-            }
         }
     }
 }

+ 0 - 6
tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs

@@ -276,12 +276,6 @@ namespace Avalonia.Controls.UnitTests
                 : base(impl)
             {
             }
-
-            protected override void HandleApplicationExiting()
-            {
-                base.HandleApplicationExiting();
-                IsClosed = true;
-            }
         }
     }
 }

+ 0 - 76
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@@ -121,75 +121,6 @@ namespace Avalonia.Controls.UnitTests
             }
         }
 
-        [Fact]
-        public void Show_Should_Add_Window_To_OpenWindows()
-        {
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                ClearOpenWindows();
-                var window = new Window();
-
-                window.Show();
-
-                Assert.Equal(new[] { window }, Application.Current.Windows);
-            }
-        }
-
-        [Fact]
-        public void Window_Should_Be_Added_To_OpenWindows_Only_Once()
-        {
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                ClearOpenWindows();
-                var window = new Window();
-
-                window.Show();
-                window.Show();
-                window.IsVisible = true;
-
-                Assert.Equal(new[] { window }, Application.Current.Windows);
-
-                window.Close();
-            }
-        }
-
-        [Fact]
-        public void Close_Should_Remove_Window_From_OpenWindows()
-        {
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                ClearOpenWindows();
-                var window = new Window();
-
-                window.Show();
-                window.Close();
-
-                Assert.Empty(Application.Current.Windows);
-            }
-        }
-
-        [Fact]
-        public void Impl_Closing_Should_Remove_Window_From_OpenWindows()
-        {
-            var windowImpl = new Mock<IWindowImpl>();
-            windowImpl.SetupProperty(x => x.Closed);
-            windowImpl.Setup(x => x.Scaling).Returns(1);
-
-            var services = TestServices.StyledWindow.With(
-                windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object));
-
-            using (UnitTestApplication.Start(services))
-            {
-                ClearOpenWindows();
-                var window = new Window();
-
-                window.Show();
-                windowImpl.Object.Closed();
-
-                Assert.Empty(Application.Current.Windows);
-            }
-        }
-
         [Fact]
         public void Closing_Should_Only_Be_Invoked_Once()
         {
@@ -420,12 +351,5 @@ namespace Avalonia.Controls.UnitTests
                 x.Scaling == 1 &&
                 x.CreateRenderer(It.IsAny<IRenderRoot>()) == renderer.Object);
         }
-
-        private void ClearOpenWindows()
-        {
-            // HACK: We really need a decent way to have "statics" that can be scoped to
-            // AvaloniaLocator scopes.
-            Application.Current.Windows.Clear();
-        }
     }
 }

+ 31 - 9
tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests_Converters.cs

@@ -5,11 +5,10 @@ using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
-using System.Text;
 using Avalonia.Controls;
 using Avalonia.Data;
 using Avalonia.Data.Converters;
-using Avalonia.Data.Core;
+using Avalonia.Layout;
 using Xunit;
 
 namespace Avalonia.Markup.UnitTests.Data
@@ -21,7 +20,30 @@ namespace Avalonia.Markup.UnitTests.Data
         {
             var textBlock = new TextBlock
             {
-                DataContext = new MultiBindingTests_Converters.Class1(),
+                DataContext = new Class1(),
+            };
+
+            var target = new MultiBinding
+            {
+                StringFormat = "{0:0.0} + {1:00}",
+                Bindings =
+                {
+                    new Binding(nameof(Class1.Foo)),
+                    new Binding(nameof(Class1.Bar)),
+                }
+            };
+
+            textBlock.Bind(TextBlock.TextProperty, target);
+
+            Assert.Equal("1.0 + 02", textBlock.Text);
+        }
+
+        [Fact]
+        public void StringFormat_Should_Be_Applied_After_Converter()
+        {
+            var textBlock = new TextBlock
+            {
+                DataContext = new Class1(),
             };
 
             var target = new MultiBinding
@@ -30,8 +52,8 @@ namespace Avalonia.Markup.UnitTests.Data
                 Converter = new SumOfDoublesConverter(),
                 Bindings =
                 {
-                    new Binding(nameof(MultiBindingTests_Converters.Class1.Foo)),
-                    new Binding(nameof(MultiBindingTests_Converters.Class1.Bar)),
+                    new Binding(nameof(Class1.Foo)),
+                    new Binding(nameof(Class1.Bar)),
                 }
             };
 
@@ -45,7 +67,7 @@ namespace Avalonia.Markup.UnitTests.Data
         {
             var textBlock = new TextBlock
             {
-                DataContext = new MultiBindingTests_Converters.Class1(),
+                DataContext = new Class1(),
             };
             
             var target = new MultiBinding
@@ -54,12 +76,12 @@ namespace Avalonia.Markup.UnitTests.Data
                 Converter = new SumOfDoublesConverter(),
                 Bindings =
                 {
-                    new Binding(nameof(MultiBindingTests_Converters.Class1.Foo)),
-                    new Binding(nameof(MultiBindingTests_Converters.Class1.Bar)),
+                    new Binding(nameof(Class1.Foo)),
+                    new Binding(nameof(Class1.Bar)),
                 }
             };
 
-            textBlock.Bind(TextBlock.WidthProperty, target);
+            textBlock.Bind(Layoutable.WidthProperty, target);
             
             Assert.Equal(3.0, textBlock.Width);
         }

+ 19 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@@ -907,6 +907,25 @@ do we need it?")]
             }
         }
 
+        [Fact]
+        public void Slider_Properties_Can_Be_Set_In_Any_Order()
+        {
+            using (UnitTestApplication.Start(TestServices.MockWindowingPlatform))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'>
+    <Slider Width='400' Value='500' Minimum='0' Maximum='1000'/>
+</Window>";
+
+                var window = AvaloniaXamlLoader.Parse<Window>(xaml);
+                var slider = (Slider)window.Content;
+
+                Assert.Equal(0, slider.Minimum);
+                Assert.Equal(1000, slider.Maximum);
+                Assert.Equal(500, slider.Value);
+            }
+        }
+
         private class SelectedItemsViewModel : INotifyPropertyChanged
         {
             public string[] Items { get; set; }

+ 31 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs

@@ -331,6 +331,35 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
             }
         }
 
+        [Fact(Skip="Issue #2592")]
+        public void MultiBinding_To_TextBlock_Text_With_StringConverter_Works()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow))
+            {
+                var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+        xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
+    <TextBlock Name='textBlock'>
+        <TextBlock.Text>
+            <MultiBinding StringFormat='\{0\} \{1\}!'>
+                <Binding Path='Greeting1'/>
+                <Binding Path='Greeting2'/>
+            </MultiBinding>
+        </TextBlock.Text>
+    </TextBlock> 
+</Window>";
+                var loader = new AvaloniaXamlLoader();
+                var window = (Window)loader.Load(xaml);
+                var textBlock = window.FindControl<TextBlock>("textBlock");
+
+                textBlock.DataContext = new WindowViewModel();
+                window.ApplyTemplate();
+
+                Assert.Equal("Hello World!", textBlock.Text);
+            }
+        }
+
         [Fact]
         public void Binding_OneWayToSource_Works()
         {
@@ -356,6 +385,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
         private class WindowViewModel
         {
             public bool ShowInTaskbar { get; set; }
+            public string Greeting1 { get; set; } = "Hello";
+            public string Greeting2 { get; set; } = "World";
         }
     }
 }

+ 1 - 2
tests/Avalonia.UnitTests/UnitTestApplication.cs

@@ -66,8 +66,7 @@ namespace Avalonia.UnitTests
                 .Bind<IStandardCursorFactory>().ToConstant(Services.StandardCursorFactory)
                 .Bind<IStyler>().ToConstant(Services.Styler)
                 .Bind<IWindowingPlatform>().ToConstant(Services.WindowingPlatform)
-                .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
-                .Bind<IApplicationLifecycle>().ToConstant(this);
+                .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
             var styles = Services.Theme?.Invoke();
 
             if (styles != null)