1
0
Эх сурвалжийг харах

Avalonia.Native TopLevel embedding (#15932)

* Introduce Avalonia.Native TopLevelImpl

* Update Avalonia.Native.csproj

Revert change
Benedikt Stebner 1 жил өмнө
parent
commit
143399f65a
60 өөрчлөгдсөн 1378 нэмэгдсэн , 995 устгасан
  1. 14 15
      Avalonia.sln
  2. 13 0
      Avalonia.sln.DotSettings
  3. 8 4
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  4. 1 1
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme
  5. 3 3
      native/Avalonia.Native/src/OSX/AvnView.h
  6. 27 21
      native/Avalonia.Native/src/OSX/AvnView.mm
  7. 2 5
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  8. 4 0
      native/Avalonia.Native/src/OSX/INSWindowHolder.h
  9. 0 9
      native/Avalonia.Native/src/OSX/PopupImpl.h
  10. 5 7
      native/Avalonia.Native/src/OSX/PopupImpl.mm
  11. 76 0
      native/Avalonia.Native/src/OSX/TopLevelImpl.h
  12. 251 0
      native/Avalonia.Native/src/OSX/TopLevelImpl.mm
  13. 15 41
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  14. 39 199
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  15. 13 21
      native/Avalonia.Native/src/OSX/WindowImpl.h
  16. 21 45
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  17. 1 1
      native/Avalonia.Native/src/OSX/automation.mm
  18. 2 0
      native/Avalonia.Native/src/OSX/common.h
  19. 22 0
      native/Avalonia.Native/src/OSX/main.mm
  20. 4 1
      nukebuild/_build.csproj.DotSettings
  21. 2 0
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  22. 1 0
      src/Android/Avalonia.Android/Stubs.cs
  23. 1 1
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  24. 4 0
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  25. 19 11
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  26. 5 16
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  27. 2 0
      src/Avalonia.Controls/Platform/IWindowingPlatform.cs
  28. 6 0
      src/Avalonia.Controls/Platform/PlatformManager.cs
  29. 17 6
      src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs
  30. 1 9
      src/Avalonia.Controls/TopLevel.cs
  31. 11 1
      src/Avalonia.Controls/WindowBase.cs
  32. 1 0
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
  33. 4 4
      src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs
  34. 11 1
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  35. 2 2
      src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs
  36. 4 4
      src/Avalonia.Native/DeferredFramebuffer.cs
  37. 15 0
      src/Avalonia.Native/EmbeddableTopLevelImpl.cs
  38. 4 4
      src/Avalonia.Native/Metal.cs
  39. 25 6
      src/Avalonia.Native/PopupImpl.cs
  40. 6 6
      src/Avalonia.Native/SystemDialogs.cs
  41. 557 0
      src/Avalonia.Native/TopLevelImpl.cs
  42. 9 7
      src/Avalonia.Native/WindowImpl.cs
  43. 47 499
      src/Avalonia.Native/WindowImplBase.cs
  44. 60 39
      src/Avalonia.Native/avn.idl
  45. 2 0
      src/Avalonia.X11/X11Platform.cs
  46. 3 0
      src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
  47. 6 0
      src/Browser/Avalonia.Browser/WindowingPlatform.cs
  48. 1 0
      src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs
  49. 3 0
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  50. 2 0
      src/Tizen/Avalonia.Tizen/Stubs.cs
  51. 4 0
      src/Tizen/Avalonia.Tizen/TopLevelImpl.cs
  52. 1 1
      src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs
  53. 2 2
      src/Windows/Avalonia.Win32/PopupImpl.cs
  54. 7 2
      src/Windows/Avalonia.Win32/ScreenImpl.cs
  55. 2 0
      src/Windows/Avalonia.Win32/Win32Platform.cs
  56. 1 1
      src/Windows/Avalonia.Win32/WindowImpl.cs
  57. 3 0
      src/iOS/Avalonia.iOS/AvaloniaView.cs
  58. 1 0
      src/iOS/Avalonia.iOS/Stubs.cs
  59. 3 0
      tests/Avalonia.UnitTests/CompositorTestServices.cs
  60. 2 0
      tests/Avalonia.UnitTests/MockWindowingPlatform.cs

+ 14 - 15
Avalonia.sln

@@ -116,19 +116,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
 		build\SourceGenerators.props = build\SourceGenerators.props
 		build\SourceLink.props = build\SourceLink.props
 		build\System.Memory.props = build\System.Memory.props
+		build\TargetFrameworks.props = build\TargetFrameworks.props
 		build\TrimmingEnable.props = build\TrimmingEnable.props
 		build\UnitTests.NetFX.props = build\UnitTests.NetFX.props
-		build\XUnit.props = build\XUnit.props
-		build\TargetFrameworks.props = build\TargetFrameworks.props
 		build\WarnAsErrors.props = build\WarnAsErrors.props
+		build\XUnit.props = build\XUnit.props
 	EndProjectSection
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
 	ProjectSection(SolutionItems) = preProject
 		build\BuildTargets.targets = build\BuildTargets.targets
+		build\DevSingleProject.targets = build\DevSingleProject.targets
 		build\LegacyProject.targets = build\LegacyProject.targets
 		build\UnitTests.NetCore.targets = build\UnitTests.NetCore.targets
-		build\DevSingleProject.targets = build\DevSingleProject.targets
 	EndProjectSection
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}"
@@ -232,8 +232,8 @@ EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}"
 	ProjectSection(SolutionItems) = preProject
 		.editorconfig = .editorconfig
-		azure-pipelines.yml = azure-pipelines.yml
 		azure-pipelines-integrationtests.yml = azure-pipelines-integrationtests.yml
+		azure-pipelines.yml = azure-pipelines.yml
 		CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
 		CONTRIBUTING.md = CONTRIBUTING.md
 		Directory.Build.props = Directory.Build.props
@@ -283,24 +283,23 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Tizen", "sam
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Metal", "src\Avalonia.Metal\Avalonia.Metal.csproj", "{60B4ED1F-ECFA-453B-8A70-1788261C8355}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Build.Tasks.UnitTest", "tests\Avalonia.Build.Tasks.UnitTest\Avalonia.Build.Tasks.UnitTest.csproj", "{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Build.Tasks.UnitTest", "tests\Avalonia.Build.Tasks.UnitTest\Avalonia.Build.Tasks.UnitTest.csproj", "{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestFiles", "TestFiles", "{9D6AEF22-221F-4F4B-B335-A4BA510F002C}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildTasks", "BuildTasks", "{5BF0C3B8-E595-4940-AB30-2DA206C2F085}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PInvoke", "tests\TestFiles\BuildTasks\PInvoke\PInvoke.csproj", "{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PInvoke", "tests\TestFiles\BuildTasks\PInvoke\PInvoke.csproj", "{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}"
 EndProject
-
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnloadableAssemblyLoadContext", "UnloadableAssemblyLoadContext", "{9CCA131B-DE95-4D44-8788-C3CAE28574CD}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnloadableAssemblyLoadContext", "samples\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContext.csproj", "{D7FE3E0F-3FE0-4F87-A2F5-24F1454D84C0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnloadableAssemblyLoadContext", "samples\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContext.csproj", "{D7FE3E0F-3FE0-4F87-A2F5-24F1454D84C0}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnloadableAssemblyLoadContextPlug", "samples\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContextPlug\UnloadableAssemblyLoadContextPlug.csproj", "{DA5F1FF9-4259-4C54-B443-85CFA226EE6A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnloadableAssemblyLoadContextPlug", "samples\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContextPlug\UnloadableAssemblyLoadContextPlug.csproj", "{DA5F1FF9-4259-4C54-B443-85CFA226EE6A}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Vulkan", "src\Avalonia.Vulkan\Avalonia.Vulkan.csproj", "{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Vulkan", "src\Avalonia.Vulkan\Avalonia.Vulkan.csproj", "{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.RenderTests.WpfCompare", "tests\Avalonia.RenderTests.WpfCompare\Avalonia.RenderTests.WpfCompare.csproj", "{9AE1B827-21AC-4063-AB22-C8804B7F931E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.RenderTests.WpfCompare", "tests\Avalonia.RenderTests.WpfCompare\Avalonia.RenderTests.WpfCompare.csproj", "{9AE1B827-21AC-4063-AB22-C8804B7F931E}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -678,10 +677,6 @@ Global
 		{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.Build.0 = Release|Any CPU
-		{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Release|Any CPU.Build.0 = Release|Any CPU
 		{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -698,6 +693,10 @@ Global
 		{DA5F1FF9-4259-4C54-B443-85CFA226EE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{DA5F1FF9-4259-4C54-B443-85CFA226EE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{DA5F1FF9-4259-4C54-B443-85CFA226EE6A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Release|Any CPU.Build.0 = Release|Any CPU
 		{9AE1B827-21AC-4063-AB22-C8804B7F931E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{9AE1B827-21AC-4063-AB22-C8804B7F931E}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{9AE1B827-21AC-4063-AB22-C8804B7F931E}.Release|Any CPU.ActiveCfg = Release|Any CPU

+ 13 - 0
Avalonia.sln.DotSettings

@@ -36,7 +36,20 @@
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypeParameters/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="T" Suffix="" Style="AaBb" /&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypesAndNamespaces/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="s_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=2c62818f_002D621b_002D4425_002Dadc9_002D78611099bfcb/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Type parameters"&gt;&lt;ElementKinds&gt;&lt;Kind Name="TYPE_PARAMETER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="T" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=61a991a4_002Dd0a3_002D4d19_002D90a5_002Df8f4d75c30c1/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local variables"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_VARIABLE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=669e5282_002Dfb4b_002D4e90_002D91e7_002D07d269d04b60/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=8a85b61a_002D1024_002D4f87_002Db9ef_002D1fdae19930a1/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Parameters"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PARAMETER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=8b8504e3_002Df0be_002D4c14_002D9103_002Dc732f2bddc15/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Enum members"&gt;&lt;ElementKinds&gt;&lt;Kind Name="ENUM_MEMBER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="NAMESPACE" /&gt;&lt;Kind Name="CLASS" /&gt;&lt;Kind Name="STRUCT" /&gt;&lt;Kind Name="ENUM" /&gt;&lt;Kind Name="DELEGATE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a7a3339e_002D4e89_002D4319_002D9735_002Da9dc4cb74cc7/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Interfaces"&gt;&lt;ElementKinds&gt;&lt;Kind Name="INTERFACE" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=c873eafb_002Dd57f_002D481d_002D8c93_002D77f6863c2f88/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="s_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002EDaemon_002ESettings_002EMigration_002ESwaWarningsModeSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Activatable/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Avalonia/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Fcitx/@EntryIndexedValue">True</s:Boolean>

+ 8 - 4
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@@ -17,7 +17,6 @@
 		183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; };
 		18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; };
 		18391AC16726CBC45856233B /* AvnWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839155B28B20FFB672D29C6 /* AvnWindow.mm */; };
-		18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183910513F396141938832B5 /* PopupImpl.h */; };
 		18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; };
 		18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; };
 		18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; };
@@ -61,10 +60,11 @@
 		ED3791C42862E1F40080BD62 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */; };
 		ED754D262A97306B0078B4DF /* PlatformRenderTimer.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */; };
 		EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */ = {isa = PBXBuildFile; fileRef = EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */; };
+		F10084842BFF1F9E0024303E /* TopLevelImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = F10084832BFF1F9E0024303E /* TopLevelImpl.h */; };
+		F10084862BFF1FB40024303E /* TopLevelImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = F10084852BFF1FB40024303E /* TopLevelImpl.mm */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
-		183910513F396141938832B5 /* PopupImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupImpl.h; sourceTree = "<group>"; };
 		1839122E037567BDD1D09DEB /* WindowProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowProtocol.h; sourceTree = "<group>"; };
 		1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = "<group>"; };
 		183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = "<group>"; };
@@ -125,6 +125,8 @@
 		ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
 		ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformRenderTimer.mm; sourceTree = "<group>"; };
 		EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformSettings.mm; sourceTree = "<group>"; };
+		F10084832BFF1F9E0024303E /* TopLevelImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopLevelImpl.h; sourceTree = "<group>"; };
+		F10084852BFF1FB40024303E /* TopLevelImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TopLevelImpl.mm; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -164,9 +166,11 @@
 		AB7A61E62147C814003C5833 = {
 			isa = PBXGroup;
 			children = (
+				F10084852BFF1FB40024303E /* TopLevelImpl.mm */,
 				ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */,
 				855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */,
 				8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */,
+				F10084832BFF1F9E0024303E /* TopLevelImpl.h */,
 				8D300D68292E1E5D00320C49 /* AvnTextInputMethod.mm */,
 				8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */,
 				BC11A5BC2608D58F0017BAD0 /* automation.h */,
@@ -214,7 +218,6 @@
 				1839155B28B20FFB672D29C6 /* AvnWindow.mm */,
 				18391DB45C7D892E61BF388C /* WindowInterfaces.h */,
 				18391BB698579F40F1783F31 /* PopupImpl.mm */,
-				183910513F396141938832B5 /* PopupImpl.h */,
 				64B1EBEECBE13D8616D7C934 /* metal.mm */,
 				64B1E4FA7D9D6E5F47AA8606 /* noarc.mm */,
 				64B1E26F2B1B9C577BF52F06 /* noarc.h */,
@@ -237,6 +240,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				37155CE4233C00EB0034DCE9 /* menu.h in Headers */,
+				F10084842BFF1F9E0024303E /* TopLevelImpl.h in Headers */,
 				BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */,
 				183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */,
 				1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */,
@@ -249,7 +253,6 @@
 				18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */,
 				18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */,
 				183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */,
-				18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */,
 				64B1ECA861163C0EFF0E502B /* noarc.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -332,6 +335,7 @@
 				AB00E4F72147CA920032A60A /* main.mm in Sources */,
 				37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,
 				1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */,
+				F10084862BFF1FB40024303E /* TopLevelImpl.mm in Sources */,
 				1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */,
 				18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */,
 				18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */,

+ 1 - 1
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
    LastUpgradeVersion = "1000"
-   version = "1.3">
+   version = "1.7">
    <BuildAction
       parallelizeBuildables = "YES"
       buildImplicitDependencies = "YES">

+ 3 - 3
native/Avalonia.Native/src/OSX/AvnView.h

@@ -7,20 +7,20 @@
 
 #import <AppKit/AppKit.h>
 #include "common.h"
-#include "WindowImpl.h"
+#include "TopLevelImpl.h"
 #include "KeyTransform.h"
 
 @class AvnAccessibilityElement;
 @protocol IRenderTarget;
 
 @interface AvnView : NSView<NSTextInputClient, NSDraggingDestination, AvnTextInputMethodDelegate, CALayerDelegate>
--(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
+-(AvnView* _Nonnull) initWithParent: (TopLevelImpl* _Nonnull) parent;
 -(NSEvent* _Nonnull) lastMouseDownEvent;
 -(AvnPoint) translateLocalPoint:(AvnPoint)pt;
 -(void) onClosed;
 
 -(AvnPlatformResizeReason) getResizeReason;
 -(void) setResizeReason:(AvnPlatformResizeReason)reason;
--(void) setRenderTarget:(NSObject<IRenderTarget>*)target;
+-(void) setRenderTarget:(NSObject<IRenderTarget>* _Nonnull)target;
 + (AvnPoint)toAvnPoint:(CGPoint)p;
 @end

+ 27 - 21
native/Avalonia.Native/src/OSX/AvnView.mm

@@ -7,10 +7,11 @@
 #include "AvnView.h"
 #include "automation.h"
 #import "WindowInterfaces.h"
+#import "WindowImpl.h"
 
 @implementation AvnView
 {
-    ComPtr<WindowBaseImpl> _parent;
+    ComPtr<TopLevelImpl> _parent;
     NSTrackingArea* _area;
     bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed;
     AvnInputModifiers _modifierState;
@@ -67,7 +68,7 @@
     [self updateLayer];
 }
 
--(AvnView*)  initWithParent: (WindowBaseImpl*) parent
+-(AvnView*)  initWithParent: (TopLevelImpl*) parent
 {
     self = [super init];
     [self setWantsLayer:YES];
@@ -155,7 +156,7 @@
 
         auto reason = [self inLiveResize] ? ResizeUser : _resizeReason;
 
-        _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason);
+        _parent->TopLevelEvents->Resized(FromNSSize(newSize), reason);
     }
 }
 
@@ -167,14 +168,14 @@
         return;
     }
 
-    _parent->BaseEvents->RunRenderPriorityJobs();
+    _parent->TopLevelEvents->RunRenderPriorityJobs();
 
     if (_parent == nullptr)
     {
         return;
     }
 
-    _parent->BaseEvents->Paint();
+    _parent->TopLevelEvents->Paint();
 }
 
 - (void)drawRect:(NSRect)dirtyRect
@@ -207,7 +208,7 @@
 
     if(_parent != nullptr)
     {
-        _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
+        _parent->TopLevelEvents->ScalingChanged([[self window] backingScaleFactor]);
     }
 
     [super viewDidChangeBackingProperties];
@@ -219,19 +220,24 @@
     {
         return TRUE;
     }
+    
+    id<AvnWindowProtocol> parentWindow = nullptr;
 
-    auto parentWindow = _parent->GetWindowProtocol();
+    if([[self window] conformsToProtocol:@protocol(AvnWindowProtocol)]){
+        parentWindow = (id<AvnWindowProtocol>)[self window];
+    }
 
-    if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents])
+    if(parentWindow != nullptr && ![parentWindow shouldTryToHandleEvents])
     {
         if(trigerInputWhenDisabled)
         {
-            auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
-
-            if(window != nullptr)
-            {
-                window->WindowEvents->GotInputWhenDisabled();
+            WindowImpl* windowImpl = dynamic_cast<WindowImpl*>(_parent.getRaw());
+            
+            if(windowImpl == nullptr){
+                return FALSE;
             }
+            
+            windowImpl->WindowEvents->GotInputWhenDisabled();
         }
 
         return TRUE;
@@ -301,7 +307,7 @@
 
     if(_parent != nullptr)
     {
-        _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta);
+        _parent->TopLevelEvents->RawMouseEvent(type, timestamp, modifiers, point, delta);
     }
 
     [super mouseMoved:event];
@@ -309,7 +315,7 @@
 
 - (BOOL) resignFirstResponder
 {
-    _parent->BaseEvents->LostFocus();
+    _parent->TopLevelEvents->LostFocus();
     return YES;
 }
 
@@ -461,7 +467,7 @@
     auto timestamp = static_cast<uint64_t>([event timestamp] * 1000);
     auto modifiers = [self getModifiers:[event modifierFlags]];
 
-    _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key, physicalKey, keySymbolUtf8);
+    _parent->TopLevelEvents->RawKeyEvent(type, timestamp, modifiers, key, physicalKey, keySymbolUtf8);
 }
 
 - (void)flagsChanged:(NSEvent *)event
@@ -521,7 +527,7 @@
 }
 
 - (bool) handleKeyDown: (NSTimeInterval) timestamp withKey:(AvnKey)key withPhysicalKey:(AvnPhysicalKey)physicalKey withModifiers:(AvnInputModifiers)modifiers withKeySymbol:(NSString*)keySymbol {
-    return _parent->BaseEvents->RawKeyEvent(KeyDown, timestamp, modifiers, key, physicalKey, [keySymbol UTF8String]);
+    return _parent->TopLevelEvents->RawKeyEvent(KeyDown, timestamp, modifiers, key, physicalKey, [keySymbol UTF8String]);
 }
 
 - (void)keyDown:(NSEvent *)event
@@ -575,7 +581,7 @@
             if(keySymbol != nullptr && key != AvnKeyEnter){
                 auto timestamp = static_cast<uint64_t>([event timestamp] * 1000);
                 
-                _parent->BaseEvents->RawTextInputEvent(timestamp, [keySymbol UTF8String]);
+                _parent->TopLevelEvents->RawTextInputEvent(timestamp, [keySymbol UTF8String]);
             }
         }
     }
@@ -707,7 +713,7 @@
         
     uint64_t timestamp = static_cast<uint64_t>([NSDate timeIntervalSinceReferenceDate] * 1000);
         
-    _parent->BaseEvents->RawTextInputEvent(timestamp, [text UTF8String]);
+    _parent->TopLevelEvents->RawTextInputEvent(timestamp, [text UTF8String]);
 }
 
 - (NSUInteger)characterIndexForPoint:(NSPoint)point
@@ -733,7 +739,7 @@
     NSDragOperation nsop = [info draggingSourceOperationMask];
 
     auto effects = ConvertDragDropEffects(nsop);
-    int reffects = (int)_parent->BaseEvents
+    int reffects = (int)_parent->TopLevelEvents
             ->DragEvent(type, point, modifiers, effects,
                     CreateClipboard([info draggingPasteboard], nil),
                     GetAvnDataObjectHandleFromDraggingInfo(info));
@@ -799,7 +805,7 @@
 {
     if (_accessibilityChild == nil)
     {
-        auto peer = _parent->BaseEvents->GetAutomationPeer();
+        auto peer = _parent->TopLevelEvents->GetAutomationPeer();
 
         if (peer == nil)
             return nil;

+ 2 - 5
native/Avalonia.Native/src/OSX/AvnWindow.mm

@@ -24,7 +24,6 @@
 #include "WindowImpl.h"
 #include "AvnView.h"
 #include "WindowInterfaces.h"
-#include "PopupImpl.h"
 
 @implementation CLASS_NAME
 {
@@ -201,8 +200,6 @@
     [self backingScaleFactor];
 }
 
-
-
 - (void)windowWillClose:(NSNotification *_Nonnull)notification
 {
     _closed = true;
@@ -231,7 +228,7 @@
 //
 // If we don't implement this, then isZoomed always returns true for a non-
 // resizable window ¯\_(ツ)_/¯
-- (NSRect)windowWillUseStandardFrame:(NSWindow*)window
+- (NSRect)windowWillUseStandardFrame:(NSWindow* _Nonnull)window
                         defaultFrame:(NSRect)newFrame {
   return newFrame;
 }
@@ -397,7 +394,7 @@
     return _parent->CanZoom();
 }
 
--(void)windowDidResignKey:(NSNotification *)notification
+-(void)windowDidResignKey:(NSNotification* _Nonnull)notification
 {
     if(_parent)
         _parent->BaseEvents->Deactivated();

+ 4 - 0
native/Avalonia.Native/src/OSX/INSWindowHolder.h

@@ -11,6 +11,10 @@
 struct INSWindowHolder
 {
     virtual NSWindow* _Nonnull GetNSWindow () = 0;
+};
+
+struct INSViewHolder
+{
     virtual AvnView* _Nonnull GetNSView () = 0;
 };
 

+ 0 - 9
native/Avalonia.Native/src/OSX/PopupImpl.h

@@ -1,9 +0,0 @@
-//
-// Created by Dan Walmsley on 06/05/2022.
-// Copyright (c) 2022 Avalonia. All rights reserved.
-//
-
-#ifndef AVALONIA_NATIVE_OSX_POPUPIMPL_H
-#define AVALONIA_NATIVE_OSX_POPUPIMPL_H
-
-#endif //AVALONIA_NATIVE_OSX_POPUPIMPL_H

+ 5 - 7
native/Avalonia.Native/src/OSX/PopupImpl.mm

@@ -12,7 +12,6 @@
 #import "WindowBaseImpl.h"
 #import "WindowProtocol.h"
 #import <AppKit/AppKit.h>
-#include "PopupImpl.h"
 
 class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup
 {
@@ -23,7 +22,7 @@ private:
     END_INTERFACE_MAP()
     virtual ~PopupImpl(){}
     ComPtr<IAvnWindowEvents> WindowEvents;
-    PopupImpl(IAvnWindowEvents* events) : WindowBaseImpl(events)
+    PopupImpl(IAvnWindowEvents* events) : TopLevelImpl(events), WindowBaseImpl(events)
     {
         WindowEvents = events;
         [Window setLevel:NSPopUpMenuWindowLevel];
@@ -35,13 +34,12 @@ protected:
     }
 
 public:
-    virtual bool ShouldTakeFocusOnShow() override
-    {
-        return false;
-    }
-
     virtual HRESULT Show(bool activate, bool isDialog) override
     {
+        auto windowProtocol = GetWindowProtocol();
+        
+        [windowProtocol setEnabled:true];
+        
         return WindowBaseImpl::Show(activate, true);
     }
 };

+ 76 - 0
native/Avalonia.Native/src/OSX/TopLevelImpl.h

@@ -0,0 +1,76 @@
+//
+//  TopLevelImpl.h
+//  Avalonia.Native.OSX
+//
+//  Created by Benedikt Stebner on 16.05.24.
+//  Copyright © 2024 Avalonia. All rights reserved.
+//
+
+#ifndef TopLevelImpl_h
+#define TopLevelImpl_h
+
+#include "rendertarget.h"
+#include "INSWindowHolder.h"
+#include "AvnTextInputMethod.h"
+#include "AutoFitContentView.h"
+#include <list>
+
+class TopLevelImpl : public virtual ComObject,
+                     public virtual IAvnTopLevel,
+                     public INSViewHolder{
+    
+public:
+    FORWARD_IUNKNOWN()
+    BEGIN_INTERFACE_MAP()
+    INTERFACE_MAP_ENTRY(IAvnTopLevel, IID_IAvnTopLevel)
+    END_INTERFACE_MAP()
+    
+    virtual ~TopLevelImpl();
+    
+    TopLevelImpl(IAvnTopLevelEvents* events);
+                         
+    virtual AvnView *GetNSView() override;
+                         
+    virtual HRESULT SetCursor(IAvnCursor* cursor) override;
+                         
+    virtual HRESULT GetScaling(double*ret) override;
+                         
+    virtual HRESULT GetClientSize(AvnSize *ret) override;
+                           
+    virtual HRESULT GetInputMethod(IAvnTextInputMethod **ppv) override;
+                           
+    virtual HRESULT ObtainNSViewHandle(void** retOut) override;
+                                                  
+    virtual HRESULT ObtainNSViewHandleRetained(void** retOut) override;
+                           
+    virtual HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget** ret) override;
+                                                  
+    virtual HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget** ret) override;
+                           
+    virtual HRESULT CreateGlRenderTarget(IAvnGlContext* context, IAvnGlSurfaceRenderTarget** ret) override;
+
+    virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override;
+                         
+    virtual HRESULT Invalidate() override;
+                         
+    virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override;
+
+    virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override;
+     
+    virtual HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode) override;
+                         
+protected:
+    NSCursor *cursor;
+    virtual void UpdateAppearance();
+                           
+public:
+    NSObject<IRenderTarget> *currentRenderTarget;
+    ComPtr<AvnTextInputMethod> InputMethod;
+    ComPtr<IAvnTopLevelEvents> TopLevelEvents;
+    AvnView *View;
+                         
+    void UpdateCursor();
+    virtual void SetClientSize(NSSize size);
+};
+
+#endif /* TopLevelImpl_h */

+ 251 - 0
native/Avalonia.Native/src/OSX/TopLevelImpl.mm

@@ -0,0 +1,251 @@
+#import <AppKit/AppKit.h>
+#import <Cocoa/Cocoa.h>
+#include "automation.h"
+#include "cursor.h"
+#include "AutoFitContentView.h"
+#include "TopLevelImpl.h"
+#include "AvnTextInputMethod.h"
+#include "AvnView.h"
+
+TopLevelImpl::~TopLevelImpl() {
+    View = nullptr;
+}
+
+TopLevelImpl::TopLevelImpl(IAvnTopLevelEvents *events) {
+    TopLevelEvents = events;
+    
+    View = [[AvnView alloc] initWithParent:this];
+    InputMethod = new AvnTextInputMethod(View);
+}
+
+HRESULT TopLevelImpl::GetScaling(double *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr)
+            return E_POINTER;
+
+        if ([View window] == nullptr) {
+            *ret = 1;
+            return S_OK;
+        }
+
+        *ret = [[View window] backingScaleFactor];
+        
+        return S_OK;
+    }
+}
+
+HRESULT TopLevelImpl::GetClientSize(AvnSize *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr)
+            return E_POINTER;
+
+        NSRect frame = [View frame];
+        
+        ret->Width = frame.size.width;
+        ret->Height = frame.size.height;
+
+        return S_OK;
+    }
+}
+
+HRESULT TopLevelImpl::GetInputMethod(IAvnTextInputMethod **retOut) {
+    START_COM_CALL;
+
+    *retOut = InputMethod;
+
+    return S_OK;
+}
+
+HRESULT TopLevelImpl::ObtainNSViewHandle(void **ret) {
+    START_COM_CALL;
+
+    if (ret == nullptr) {
+        return E_POINTER;
+    }
+
+    *ret = (__bridge void *) View;
+
+    return S_OK;
+}
+
+HRESULT TopLevelImpl::ObtainNSViewHandleRetained(void **ret) {
+    START_COM_CALL;
+
+    if (ret == nullptr) {
+        return E_POINTER;
+    }
+
+    *ret = (__bridge_retained void *) View;
+
+    return S_OK;
+}
+
+HRESULT TopLevelImpl::SetCursor(IAvnCursor *cursor) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        Cursor *avnCursor = dynamic_cast<Cursor *>(cursor);
+        this->cursor = avnCursor->GetNative();
+        UpdateCursor();
+
+        if (avnCursor->IsHidden()) {
+            [NSCursor hide];
+        } else {
+            [NSCursor unhide];
+        }
+
+        return S_OK;
+    }
+}
+
+void TopLevelImpl::UpdateCursor() {
+    if (cursor != nil) {
+        [cursor set];
+    }
+}
+
+HRESULT TopLevelImpl::CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) {
+    START_COM_CALL;
+
+    if(![NSThread isMainThread])
+        return COR_E_INVALIDOPERATION;
+
+    if (View == NULL)
+        return E_FAIL;
+
+    auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: nil];
+    *ppv = [target createSoftwareRenderTarget];
+    [View setRenderTarget: target];
+    return S_OK;
+}
+
+HRESULT TopLevelImpl::CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) {
+    START_COM_CALL;
+
+    if(![NSThread isMainThread])
+        return COR_E_INVALIDOPERATION;
+
+    if (View == NULL)
+        return E_FAIL;
+
+    auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: glContext];
+    *ppv = [target createSurfaceRenderTarget];
+    [View setRenderTarget: target];
+    return S_OK;
+}
+
+HRESULT TopLevelImpl::CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) {
+    START_COM_CALL;
+
+    if(![NSThread isMainThread])
+        return COR_E_INVALIDOPERATION;
+
+    if (View == NULL)
+        return E_FAIL;
+
+    auto target = [[MetalRenderTarget alloc] initWithDevice: device];
+    [View setRenderTarget: target];
+    [target getRenderTarget: ppv];
+    return S_OK;
+}
+
+HRESULT TopLevelImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) {
+    START_COM_CALL;
+
+    if (View == NULL)
+        return E_FAIL;
+    *retOut = ::CreateNativeControlHost(View);
+    return S_OK;
+}
+
+AvnView *TopLevelImpl::GetNSView() {
+    return View;
+}
+
+HRESULT TopLevelImpl::Invalidate() {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        [View setNeedsDisplayInRect:[View frame]];
+
+        return S_OK;
+    }
+}
+
+HRESULT TopLevelImpl::PointToClient(AvnPoint point, AvnPoint *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr) {
+            return E_POINTER;
+        }
+
+        auto window = [View window];
+        
+        if(window == nullptr){
+            ret = &point;
+            
+            return S_OK;
+        }
+        
+        point = ConvertPointY(point);
+        NSRect convertRect = [window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)];
+        auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y);
+
+        *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)];
+
+        return S_OK;
+    }
+}
+
+HRESULT TopLevelImpl::PointToScreen(AvnPoint point, AvnPoint *ret) {
+    START_COM_CALL;
+
+    @autoreleasepool {
+        if (ret == nullptr) {
+            return E_POINTER;
+        }
+        
+        auto window = [View window];
+        
+        if(window == nullptr){
+            ret = &point;
+            
+            return S_OK;
+        }
+
+        auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]);
+        NSRect convertRect = [window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)];
+        auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y));
+        *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint));
+
+        return S_OK;
+    }
+}
+
+HRESULT TopLevelImpl::SetTransparencyMode(AvnWindowTransparencyMode mode) {
+    START_COM_CALL;
+
+    return S_OK;
+}
+
+void TopLevelImpl::UpdateAppearance() {
+    
+}
+
+void TopLevelImpl::SetClientSize(NSSize size){
+    [View setFrameSize:size];
+}
+
+extern IAvnTopLevel* CreateAvnTopLevel(IAvnTopLevelEvents* events)
+{
+    @autoreleasepool
+    {
+        IAvnTopLevel* ptr = (IAvnTopLevel*)new TopLevelImpl(events);
+        return ptr;
+    }
+}

+ 15 - 41
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@@ -9,19 +9,21 @@
 #include "rendertarget.h"
 #include "INSWindowHolder.h"
 #include "AvnTextInputMethod.h"
+#include "TopLevelImpl.h"
+#include <list>
 
-@class AutoFitContentView;
 @class AvnMenu;
 @protocol AvnWindowProtocol;
 
-class WindowBaseImpl : public virtual ComObject,
+class WindowBaseImpl : public virtual TopLevelImpl,
                        public virtual IAvnWindowBase,
                        public INSWindowHolder {
 
 public:
     FORWARD_IUNKNOWN()
 
-BEGIN_INTERFACE_MAP()
+    BEGIN_INTERFACE_MAP()
+        INHERIT_INTERFACE_MAP(TopLevelImpl)
         INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase)
     END_INTERFACE_MAP()
 
@@ -33,14 +35,8 @@ BEGIN_INTERFACE_MAP()
 
     virtual HRESULT ObtainNSWindowHandleRetained(void **ret) override;
 
-    virtual HRESULT ObtainNSViewHandle(void **ret) override;
-
-    virtual HRESULT ObtainNSViewHandleRetained(void **ret) override;
-
     virtual NSWindow *GetNSWindow() override;
 
-    virtual AvnView *GetNSView() override;
-
     virtual HRESULT Show(bool activate, bool isDialog) override;
 
     virtual bool IsShown ();
@@ -55,18 +51,12 @@ BEGIN_INTERFACE_MAP()
 
     virtual HRESULT Close() override;
 
-    virtual HRESULT GetClientSize(AvnSize *ret) override;
-
     virtual HRESULT GetFrameSize(AvnSize *ret) override;
 
-    virtual HRESULT GetScaling(double *ret) override;
-
     virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) override;
 
     virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override;
 
-    virtual HRESULT Invalidate(__attribute__((unused)) AvnRect rect) override;
-
     virtual HRESULT SetMainMenu(IAvnMenu *menu) override;
 
     virtual HRESULT BeginMoveDrag() override;
@@ -77,49 +67,33 @@ BEGIN_INTERFACE_MAP()
 
     virtual HRESULT SetPosition(AvnPoint point) override;
 
-    virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override;
-
-    virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override;
-
-    virtual HRESULT SetCursor(IAvnCursor *cursor) override;
-
-    virtual void UpdateCursor();
-
-    virtual HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) override;
-
-    virtual HRESULT CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) override;
-
-    virtual HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) override;
-
-    virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override;
-
-    virtual HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode) override;
-
     virtual HRESULT SetFrameThemeVariant(AvnPlatformThemeVariant variant) override;
 
     virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
             IAvnClipboard *clipboard, IAvnDndResultCallback *cb,
             void *sourceHandle) override;
 
+    virtual HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode) override;
+                           
     virtual bool IsModal();
 
     id<AvnWindowProtocol> GetWindowProtocol ();
                            
     virtual void BringToFront ();
-                           
-    virtual HRESULT GetInputMethod(IAvnTextInputMethod **retOut) override;
 
     virtual bool CanZoom() { return false; }
                            
+    virtual HRESULT SetParent(IAvnWindowBase* parent) override;
+                           
 protected:
     virtual NSWindowStyleMask CalculateStyleMask() = 0;
-    virtual void UpdateStyle();
+    virtual void UpdateAppearance() override;
+    virtual void SetClientSize(NSSize size) override;
 
 private:
     void CreateNSWindow (bool isDialog);
     void CleanNSWindow ();
 
-    NSCursor *cursor;
     bool hasPosition;
     NSSize lastSize;
     NSSize lastMinSize;
@@ -128,16 +102,16 @@ private:
     bool _inResize;
 
 protected:
-    AvnPoint lastPositionSet;
     AutoFitContentView *StandardContainer;
+    AvnPoint lastPositionSet;
     bool _shown;
+    std::list<WindowBaseImpl*> _children;
+    bool _isModal;
 
 public:
-    NSObject <IRenderTarget> *currentRenderTarget;
+    WindowBaseImpl* Parent;
     NSWindow * Window;
     ComPtr<IAvnWindowBaseEvents> BaseEvents;
-    ComPtr<AvnTextInputMethod> InputMethod;
-    AvnView *View;
 };
 
 #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H

+ 39 - 199
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@@ -15,22 +15,22 @@
 #import "WindowProtocol.h"
 #import "WindowInterfaces.h"
 #include "WindowBaseImpl.h"
+#include "WindowImpl.h"
 #include "AvnTextInputMethod.h"
 #include "AvnView.h"
 
+@class AutoFitContentView;
 
 WindowBaseImpl::~WindowBaseImpl() {
     View = nullptr;
     Window = nullptr;
 }
 
-WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel) {
+WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel) : TopLevelImpl(events) {
+    _children = std::list<WindowBaseImpl*>();
     _shown = false;
     _inResize = false;
     BaseEvents = events;
-    View = [[AvnView alloc] initWithParent:this];
-    InputMethod = new AvnTextInputMethod(View);
-    StandardContainer = [[AutoFitContentView new] initWithContent:View];
 
     lastPositionSet = { 0, 0 };
     hasPosition = false;
@@ -41,6 +41,8 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel) {
     
     CreateNSWindow(usePanel);
     
+    StandardContainer = [[AutoFitContentView new] initWithContent:View];
+    
     [Window setContentView:StandardContainer];
     [Window setBackingType:NSBackingStoreBuffered];
     [Window setContentMinSize:lastMinSize];
@@ -48,38 +50,10 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, bool usePanel) {
     [Window setOpaque:false];
 }
 
-HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) {
-    START_COM_CALL;
-
-    if (ret == nullptr) {
-        return E_POINTER;
-    }
-
-    *ret = (__bridge void *) View;
-
-    return S_OK;
-}
-
-HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) {
-    START_COM_CALL;
-
-    if (ret == nullptr) {
-        return E_POINTER;
-    }
-
-    *ret = (__bridge_retained void *) View;
-
-    return S_OK;
-}
-
 NSWindow *WindowBaseImpl::GetNSWindow() {
     return Window;
 }
 
-AvnView *WindowBaseImpl::GetNSView() {
-    return View;
-}
-
 HRESULT WindowBaseImpl::ObtainNSWindowHandleRetained(void **ret) {
     START_COM_CALL;
 
@@ -117,7 +91,7 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
         auto collectionBehavior = [Window collectionBehavior];
         [Window setCollectionBehavior:collectionBehavior & ~NSWindowCollectionBehaviorFullScreenPrimary];
 
-        UpdateStyle();
+        UpdateAppearance();
         
         [Window invalidateShadow];
 
@@ -219,20 +193,6 @@ HRESULT WindowBaseImpl::Close() {
     }
 }
 
-HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) {
-    START_COM_CALL;
-
-    @autoreleasepool {
-        if (ret == nullptr)
-            return E_POINTER;
-
-        ret->Width = lastSize.width;
-        ret->Height = lastSize.height;
-
-        return S_OK;
-    }
-}
-
 HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) {
     START_COM_CALL;
 
@@ -250,23 +210,6 @@ HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) {
     }
 }
 
-HRESULT WindowBaseImpl::GetScaling(double *ret) {
-    START_COM_CALL;
-
-    @autoreleasepool {
-        if (ret == nullptr)
-            return E_POINTER;
-
-        if (Window == nullptr) {
-            *ret = 1;
-            return S_OK;
-        }
-
-        *ret = [Window backingScaleFactor];
-        return S_OK;
-    }
-}
-
 HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) {
     START_COM_CALL;
 
@@ -330,7 +273,7 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso
 
                 lastSize = NSSize{x, y};
 
-                [Window setContentSize:lastSize];
+                SetClientSize(lastSize);
                 [Window invalidateShadow];
             }
         }
@@ -342,16 +285,6 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso
     }
 }
 
-HRESULT WindowBaseImpl::Invalidate(__attribute__((unused)) AvnRect rect) {
-    START_COM_CALL;
-
-    @autoreleasepool {
-        [View setNeedsDisplayInRect:[View frame]];
-
-        return S_OK;
-    }
-}
-
 HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) {
     START_COM_CALL;
 
@@ -431,119 +364,6 @@ HRESULT WindowBaseImpl::SetPosition(AvnPoint point) {
     }
 }
 
-HRESULT WindowBaseImpl::PointToClient(AvnPoint point, AvnPoint *ret) {
-    START_COM_CALL;
-
-    @autoreleasepool {
-        if (ret == nullptr) {
-            return E_POINTER;
-        }
-
-        point = ConvertPointY(point);
-        NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)];
-        auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y);
-
-        *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)];
-
-        return S_OK;
-    }
-}
-
-HRESULT WindowBaseImpl::PointToScreen(AvnPoint point, AvnPoint *ret) {
-    START_COM_CALL;
-
-    @autoreleasepool {
-        if (ret == nullptr) {
-            return E_POINTER;
-        }
-
-        auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]);
-        NSRect convertRect = [Window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)];
-        auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y));
-        *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint));
-
-        return S_OK;
-    }
-}
-
-HRESULT WindowBaseImpl::SetCursor(IAvnCursor *cursor) {
-    START_COM_CALL;
-
-    @autoreleasepool {
-        Cursor *avnCursor = dynamic_cast<Cursor *>(cursor);
-        this->cursor = avnCursor->GetNative();
-        UpdateCursor();
-
-        if (avnCursor->IsHidden()) {
-            [NSCursor hide];
-        } else {
-            [NSCursor unhide];
-        }
-
-        return S_OK;
-    }
-}
-
-void WindowBaseImpl::UpdateCursor() {
-    if (cursor != nil) {
-        [cursor set];
-    }
-}
-
-HRESULT WindowBaseImpl::CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) {
-    START_COM_CALL;
-
-    if(![NSThread isMainThread])
-        return COR_E_INVALIDOPERATION;
-
-    if (View == NULL)
-        return E_FAIL;
-
-    auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: nil];
-    *ppv = [target createSoftwareRenderTarget];
-    [View setRenderTarget: target];
-    return S_OK;
-}
-
-HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) {
-    START_COM_CALL;
-
-    if(![NSThread isMainThread])
-        return COR_E_INVALIDOPERATION;
-
-    if (View == NULL)
-        return E_FAIL;
-
-    auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: glContext];
-    *ppv = [target createSurfaceRenderTarget];
-    [View setRenderTarget: target];
-    return S_OK;
-}
-
-HRESULT WindowBaseImpl::CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) {
-    START_COM_CALL;
-
-    if(![NSThread isMainThread])
-        return COR_E_INVALIDOPERATION;
-
-    if (View == NULL)
-        return E_FAIL;
-
-    auto target = [[MetalRenderTarget alloc] initWithDevice: device];
-    [View setRenderTarget: target];
-    [target getRenderTarget: ppv];
-    return S_OK;
-}
-
-HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) {
-    START_COM_CALL;
-
-    if (View == NULL)
-        return E_FAIL;
-    *retOut = ::CreateNativeControlHost(View);
-    return S_OK;
-}
-
 HRESULT WindowBaseImpl::SetTransparencyMode(AvnWindowTransparencyMode mode) {
     START_COM_CALL;
 
@@ -619,10 +439,14 @@ bool WindowBaseImpl::IsModal() {
     return false;
 }
 
-void WindowBaseImpl::UpdateStyle() {
+void WindowBaseImpl::UpdateAppearance() {
     [Window setStyleMask:CalculateStyleMask()];
 }
 
+void WindowBaseImpl::SetClientSize(NSSize size){
+    [Window setContentSize:lastSize];
+}
+
 void WindowBaseImpl::CleanNSWindow() {
     if(Window != nullptr) {
         [GetWindowProtocol() disconnectParent];
@@ -654,19 +478,35 @@ void WindowBaseImpl::BringToFront()
     // do nothing.
 }
 
-HRESULT WindowBaseImpl::GetInputMethod(IAvnTextInputMethod **retOut) {
+HRESULT WindowBaseImpl::SetParent(IAvnWindowBase *parent) {
     START_COM_CALL;
 
-    *retOut = InputMethod;
+    @autoreleasepool {
+        if(Parent != nullptr)
+        {
+            Parent->_children.remove(this);
+        }
+
+        auto cparent = dynamic_cast<WindowImpl *>(parent);
+        
+        Parent = cparent;
 
-    return S_OK;
-}
+        _isModal = Parent != nullptr;
+        
+        if(Parent != nullptr && Window != nullptr){
+            // If one tries to show a child window with a minimized parent window, then the parent window will be
+            // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
+            // state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
+            if (cparent->WindowState() == Minimized)
+                cparent->SetWindowState(Normal);
+
+            [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
+                
+            cparent->_children.push_back(this);
+                
+            UpdateAppearance();
+        }
 
-extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events)
-{
-    @autoreleasepool
-    {
-        IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events);
-        return ptr;
+        return S_OK;
     }
 }

+ 13 - 21
native/Avalonia.Native/src/OSX/WindowImpl.h

@@ -8,26 +8,9 @@
 
 #import "WindowBaseImpl.h"
 #include "IWindowStateChanged.h"
-#include <list>
-
 class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
 {
-private:
-    bool _isEnabled;
-    bool _canResize;
-    bool _fullScreenActive;
-    SystemDecorations _decorations;
-    AvnWindowState _lastWindowState;
-    AvnWindowState _actualWindowState;
-    bool _inSetWindowState;
-    NSRect _preZoomSize;
-    bool _transitioningWindowState;
-    bool _isClientAreaExtended;
-    bool _isModal;
-    WindowImpl* _parent;
-    std::list<WindowImpl*> _children;
-    AvnExtendClientAreaChromeHints _extendClientHints;
-
+public:
     FORWARD_IUNKNOWN()
 BEGIN_INTERFACE_MAP()
         INHERIT_INTERFACE_MAP(WindowBaseImpl)
@@ -45,8 +28,6 @@ BEGIN_INTERFACE_MAP()
 
     virtual HRESULT SetEnabled (bool enable) override;
 
-    virtual HRESULT SetParent (IAvnWindow* parent) override;
-
     void StartStateTransition () override ;
 
     void EndStateTransition () override ;
@@ -103,12 +84,23 @@ BEGIN_INTERFACE_MAP()
     
 protected:
     virtual NSWindowStyleMask CalculateStyleMask() override;
-    void UpdateStyle () override;
+    virtual void UpdateAppearance() override;
 
 private:
     void ZOrderChildWindows();
     void OnInitialiseNSWindow();
     NSString *_lastTitle;
+    bool _isEnabled;
+    bool _canResize;
+    bool _fullScreenActive;
+    SystemDecorations _decorations;
+    AvnWindowState _lastWindowState;
+    AvnWindowState _actualWindowState;
+    bool _inSetWindowState;
+    NSRect _preZoomSize;
+    bool _transitioningWindowState;
+    bool _isClientAreaExtended;
+    AvnExtendClientAreaChromeHints _extendClientHints;
 };
 
 #endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H

+ 21 - 45
native/Avalonia.Native/src/OSX/WindowImpl.mm

@@ -8,10 +8,10 @@
 #include "AvnView.h"
 #include "automation.h"
 #include "WindowProtocol.h"
+#include "WindowImpl.h"
 
-WindowImpl::WindowImpl(IAvnWindowEvents *events) : WindowBaseImpl(events) {
+WindowImpl::WindowImpl(IAvnWindowEvents *events) : TopLevelImpl(events), WindowBaseImpl(events, false) {
     _isEnabled = true;
-    _children = std::list<WindowImpl*>();
     _isClientAreaExtended = false;
     _extendClientHints = AvnDefaultChrome;
     _fullScreenActive = false;
@@ -22,7 +22,7 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events) : WindowBaseImpl(events) {
     _lastWindowState = Normal;
     _actualWindowState = Normal;
     _lastTitle = @"";
-    _parent = nullptr;
+    Parent = nullptr;
     WindowEvents = events;
 
     [Window setHasShadow:true];
@@ -69,40 +69,7 @@ HRESULT WindowImpl::SetEnabled(bool enable) {
     @autoreleasepool {
         _isEnabled = enable;
         [GetWindowProtocol() setEnabled:enable];
-        UpdateStyle();
-        return S_OK;
-    }
-}
-
-HRESULT WindowImpl::SetParent(IAvnWindow *parent) {
-    START_COM_CALL;
-
-    @autoreleasepool {
-        if(_parent != nullptr)
-        {
-            _parent->_children.remove(this);
-        }
-
-        auto cparent = dynamic_cast<WindowImpl *>(parent);
-        
-        _parent = cparent;
-
-        _isModal = _parent != nullptr;
-        
-        if(_parent != nullptr && Window != nullptr){
-            // If one tries to show a child window with a minimized parent window, then the parent window will be
-            // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
-            // state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
-            if (cparent->WindowState() == Minimized)
-                cparent->SetWindowState(Normal);
-
-            [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
-                
-            cparent->_children.push_back(this);
-                
-            UpdateStyle();
-        }
-
+        UpdateAppearance();
         return S_OK;
     }
 }
@@ -156,12 +123,12 @@ bool WindowImpl::CanBecomeKeyWindow()
 
 void WindowImpl::StartStateTransition() {
     _transitioningWindowState = true;
-    UpdateStyle();
+    UpdateAppearance();
 }
 
 void WindowImpl::EndStateTransition() {
     _transitioningWindowState = false;
-    UpdateStyle();
+    UpdateAppearance();
 
     // Ensure correct order of child windows after fullscreen transition.
     ZOrderChildWindows();
@@ -236,7 +203,7 @@ HRESULT WindowImpl::SetCanResize(bool value) {
 
     @autoreleasepool {
         _canResize = value;
-        UpdateStyle();
+        UpdateAppearance();
         return S_OK;
     }
 }
@@ -252,7 +219,7 @@ HRESULT WindowImpl::SetDecorations(SystemDecorations value) {
             return S_OK;
         }
 
-        UpdateStyle();
+        UpdateAppearance();
 
         switch (_decorations) {
             case SystemDecorationsNone:
@@ -427,7 +394,7 @@ HRESULT WindowImpl::SetExtendClientArea(bool enable) {
             }
 
             [GetWindowProtocol() setIsExtended:enable];
-            UpdateStyle();
+            UpdateAppearance();
         }
 
         return S_OK;
@@ -579,7 +546,7 @@ bool WindowImpl::IsModal() {
 }
 
 bool WindowImpl::IsOwned() {
-    return _parent != nullptr;
+    return Parent != nullptr;
 }
 
 NSWindowStyleMask WindowImpl::CalculateStyleMask() {
@@ -620,8 +587,8 @@ NSWindowStyleMask WindowImpl::CalculateStyleMask() {
     return s;
 }
 
-void WindowImpl::UpdateStyle() {
-    WindowBaseImpl::UpdateStyle();
+void WindowImpl::UpdateAppearance() {
+    WindowBaseImpl::UpdateAppearance();
     
     if (Window == nil) {
         return;
@@ -642,3 +609,12 @@ void WindowImpl::UpdateStyle() {
     [zoomButton setHidden:!hasTrafficLights];
     [zoomButton setEnabled:CanZoom()];
 }
+
+extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events)
+{
+    @autoreleasepool
+    {
+        IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events);
+        return ptr;
+    }
+}

+ 1 - 1
native/Avalonia.Native/src/OSX/automation.mm

@@ -80,7 +80,7 @@ private:
             return nil;
         }
         
-        auto holder = dynamic_cast<INSWindowHolder*>(window);
+        auto holder = dynamic_cast<INSViewHolder*>(window);
         auto view = holder->GetNSView();
         return [[AvnRootAccessibilityElement alloc] initWithPeer:peer owner:view];
     }

+ 2 - 0
native/Avalonia.Native/src/OSX/common.h

@@ -11,6 +11,7 @@
 extern IAvnPlatformThreadingInterface* CreatePlatformThreading();
 extern void FreeAvnGCHandle(void* handle);
 extern void PostDispatcherCallback(IAvnActionCallback* cb);
+extern IAvnTopLevel* CreateAvnTopLevel(IAvnTopLevelEvents* events);
 extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events);
 extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events);
 extern IAvnSystemDialogs* CreateSystemDialogs();
@@ -46,6 +47,7 @@ extern NSRect ToNSRect (AvnRect r);
 extern AvnPoint ToAvnPoint (NSPoint p);
 extern AvnPoint ConvertPointY (AvnPoint p);
 extern NSSize ToNSSize (AvnSize s);
+extern AvnSize FromNSSize (NSSize s);
 #ifdef DEBUG
 #define NSDebugLog(...) NSLog(__VA_ARGS__)
 #else

+ 22 - 0
native/Avalonia.Native/src/OSX/main.mm

@@ -225,6 +225,19 @@ public:
         return (IAvnMacOptions*)new MacOptions();
     }
     
+    virtual HRESULT CreateTopLevel(IAvnTopLevelEvents* cb,
+                                           IAvnTopLevel** ppv) override {
+        START_COM_CALL;
+        
+        @autoreleasepool
+        {
+            if(cb == nullptr || ppv == nullptr)
+                return E_POINTER;
+            *ppv = CreateAvnTopLevel(cb);
+            return S_OK;
+        }
+    }
+    
     virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv)  override
     {
         START_COM_CALL;
@@ -484,6 +497,15 @@ NSSize ToNSSize (AvnSize s)
     return result;
 }
 
+AvnSize FromNSSize (NSSize s)
+{
+    AvnSize result;
+    result.Width = s.width;
+    result.Height = s.height;
+    
+    return result;
+}
+
 NSPoint ToNSPoint (AvnPoint p)
 {
     NSPoint result;

+ 4 - 1
nukebuild/_build.csproj.DotSettings

@@ -13,6 +13,8 @@
 	<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ANONYMOUSMETHOD_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
@@ -21,4 +23,5 @@
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
-	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

+ 2 - 0
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@@ -100,6 +100,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
 
         internal InvalidationAwareSurfaceView InternalView => _view;
 
+        public double DesktopScaling => RenderScaling;
+        public IScreenImpl? Screen { get; }
         public IPlatformHandle Handle => _view;
 
         public IEnumerable<object> Surfaces { get; }

+ 1 - 0
src/Android/Avalonia.Android/Stubs.cs

@@ -7,6 +7,7 @@ namespace Avalonia.Android
     internal class WindowingPlatformStub : IWindowingPlatform
     {
         public IWindowImpl CreateWindow() => throw new NotSupportedException();
+        public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow();
 
         public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException();
 

+ 1 - 1
src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs

@@ -13,7 +13,7 @@ namespace Avalonia.Controls.Embedding
         {
         }
 
-        public EmbeddableControlRoot() : base(PlatformManager.CreateEmbeddableWindow())
+        public EmbeddableControlRoot() : base(PlatformManager.CreateEmbeddableTopLevel())
         {
         }
 

+ 4 - 0
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@@ -29,6 +29,10 @@ namespace Avalonia.Controls.Embedding.Offscreen
 
         public abstract IEnumerable<object> Surfaces { get; }
 
+        public double DesktopScaling => _scaling;
+        public IScreenImpl? Screen { get; }
+        public IPlatformHandle? Handle { get; }
+
         public Size ClientSize
         {
             get => _clientSize;

+ 19 - 11
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@@ -3,9 +3,7 @@ using System.Collections.Generic;
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
-using Avalonia.Layout;
 using Avalonia.Metadata;
-using Avalonia.Rendering;
 using Avalonia.Rendering.Composition;
 
 namespace Avalonia.Platform
@@ -21,14 +19,24 @@ namespace Avalonia.Platform
     public interface ITopLevelImpl : IOptionalFeatureProvider, IDisposable
     {
         /// <summary>
-        /// Gets the client size of the toplevel.
+        /// Gets the scaling factor for Window positioning and sizing.
         /// </summary>
-        Size ClientSize { get; }
+        double DesktopScaling { get; }
+
+        /// <summary>
+        /// Gets platform specific display information
+        /// </summary>
+        IScreenImpl? Screen { get; }
 
         /// <summary>
-        /// Gets the total size of the toplevel, excluding shadows.
+        /// Get the platform handle.
         /// </summary>
-        Size? FrameSize { get; }
+        IPlatformHandle? Handle { get; }
+        
+        /// <summary>
+        /// Gets the client size of the toplevel.
+        /// </summary>
+        Size ClientSize { get; }
 
         /// <summary>
         /// Gets the scaling factor for the toplevel. This is used for rendering.
@@ -123,16 +131,16 @@ namespace Avalonia.Platform
         /// Gets the current <see cref="WindowTransparencyLevel"/> of the TopLevel.
         /// </summary>
         WindowTransparencyLevel TransparencyLevel { get; }
+        
+        /// <summary>
+        /// Gets the <see cref="AcrylicPlatformCompensationLevels"/> for the platform.        
+        /// </summary>
+        AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }
 
         /// <summary>
         /// Sets the <see cref="PlatformThemeVariant"/> on the frame if it should be dark or light.
         /// Also applies for the mobile status bar.
         /// </summary>
         void SetFrameThemeVariant(PlatformThemeVariant themeVariant);
-        
-        /// <summary>
-        /// Gets the <see cref="AcrylicPlatformCompensationLevels"/> for the platform.        
-        /// </summary>
-        AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }
     }
 }

+ 5 - 16
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@@ -1,5 +1,4 @@
 using System;
-using Avalonia.Automation.Peers;
 using Avalonia.Metadata;
 
 namespace Avalonia.Platform
@@ -7,6 +6,11 @@ namespace Avalonia.Platform
     [Unstable]
     public interface IWindowBaseImpl : ITopLevelImpl
     {
+        /// <summary>
+        /// Gets the total size of the toplevel, excluding shadows.
+        /// </summary>
+        Size? FrameSize { get; }
+        
         /// <summary>
         /// Shows the window.
         /// </summary>
@@ -18,11 +22,6 @@ namespace Avalonia.Platform
         /// Hides the window.
         /// </summary>
         void Hide();
-        
-        /// <summary>
-        /// Gets the scaling factor for Window positioning and sizing.
-        /// </summary>
-        double DesktopScaling { get; }
 
         /// <summary>
         /// Gets the position of the window in device pixels.
@@ -48,11 +47,6 @@ namespace Avalonia.Platform
         /// Gets or sets a method called when the window is activated (receives focus).
         /// </summary>
         Action? Activated { get; set; }
-
-        /// <summary>
-        /// Gets the platform window handle.
-        /// </summary>
-        IPlatformHandle Handle { get; }
        
         /// <summary>
         /// Gets a maximum client size hint for an auto-sizing window, in device-independent pixels.
@@ -63,10 +57,5 @@ namespace Avalonia.Platform
         /// Sets whether this window appears on top of all other windows
         /// </summary>
         void SetTopmost(bool value);
-
-        /// <summary>
-        /// Gets platform specific display information
-        /// </summary>
-        IScreenImpl Screen { get; }
     }
 }

+ 2 - 0
src/Avalonia.Controls/Platform/IWindowingPlatform.cs

@@ -7,6 +7,8 @@ namespace Avalonia.Platform
     {
         IWindowImpl CreateWindow();
 
+        ITopLevelImpl CreateEmbeddableTopLevel();
+        
         IWindowImpl CreateEmbeddableWindow();
 
         ITrayIconImpl? CreateTrayIcon();

+ 6 - 0
src/Avalonia.Controls/Platform/PlatformManager.cs

@@ -36,5 +36,11 @@ namespace Avalonia.Controls.Platform
             var platform = AvaloniaLocator.Current.GetRequiredService<IWindowingPlatform>();
             return platform.CreateEmbeddableWindow();
         }
+        
+        public static ITopLevelImpl CreateEmbeddableTopLevel()
+        {
+            var platform = AvaloniaLocator.Current.GetRequiredService<IWindowingPlatform>();
+            return platform.CreateEmbeddableTopLevel();
+        }
     }
 }

+ 17 - 6
src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs

@@ -12,22 +12,33 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
     [PrivateApi]
     public class ManagedPopupPositionerPopupImplHelper : IManagedPopupPositionerPopup 
     {
-        private readonly IWindowBaseImpl _parent;
+        private readonly ITopLevelImpl _parent;
 
         public delegate void MoveResizeDelegate(PixelPoint position, Size size, double scaling);
         private readonly MoveResizeDelegate _moveResize;
 
-        public ManagedPopupPositionerPopupImplHelper(IWindowBaseImpl parent, MoveResizeDelegate moveResize)
+        public ManagedPopupPositionerPopupImplHelper(ITopLevelImpl parent, MoveResizeDelegate moveResize)
         {
             _parent = parent;
             _moveResize = moveResize;
         }
 
-        public IReadOnlyList<ManagedPopupPositionerScreenInfo> Screens =>
+        public IReadOnlyList<ManagedPopupPositionerScreenInfo> Screens
+        {
+            get
+            {
+                if (_parent.Screen is null)
+                {
+                    return Array.Empty<ManagedPopupPositionerScreenInfo>();
+                }
+
+                return _parent.Screen.AllScreens
+                    .Select(s => new ManagedPopupPositionerScreenInfo(s.Bounds.ToRect(1), s.WorkingArea.ToRect(1)))
+                    .ToArray();
+            }
+        }
 
-            _parent.Screen.AllScreens
-                .Select(s => new ManagedPopupPositionerScreenInfo(s.Bounds.ToRect(1), s.WorkingArea.ToRect(1)))
-                .ToArray();
+           
 
         public Rect ParentClientAreaScreenGeometry
         {

+ 1 - 9
src/Avalonia.Controls/TopLevel.cs

@@ -232,11 +232,6 @@ namespace Avalonia.Controls
             impl.TransparencyLevelChanged = HandleTransparencyLevelChanged;
 
             CreatePlatformImplBinding(TransparencyLevelHintProperty, hint => PlatformImpl.SetTransparencyLevelHint(hint ?? Array.Empty<WindowTransparencyLevel>()));
-            CreatePlatformImplBinding(ActualThemeVariantProperty, variant =>
-            {
-                variant ??= ThemeVariant.Default;
-                PlatformImpl.SetFrameThemeVariant((PlatformThemeVariant?)variant ?? PlatformThemeVariant.Light);
-            });
 
             _keyboardNavigationHandler?.SetOwner(this);
             _accessKeyHandler?.SetOwner(this);
@@ -253,7 +248,6 @@ namespace Avalonia.Controls
             }
 
             ClientSize = impl.ClientSize;
-            FrameSize = impl.FrameSize;
 
             if (((IStyleHost)this).StylingParent is IResourceHost applicationResources)
             {
@@ -429,7 +423,7 @@ namespace Avalonia.Controls
         /// An <see cref="IPlatformHandle"/> describing the window handle, or null if the handle
         /// could not be retrieved.
         /// </returns>
-        public IPlatformHandle? TryGetPlatformHandle() => (PlatformImpl as IWindowBaseImpl)?.Handle;
+        public IPlatformHandle? TryGetPlatformHandle() => PlatformImpl?.Handle;
 
         private protected void CreatePlatformImplBinding<TValue>(StyledProperty<TValue> property, Action<TValue> onValue)
         {
@@ -728,7 +722,6 @@ namespace Avalonia.Controls
         internal virtual void HandleResized(Size clientSize, WindowResizeReason reason)
         {
             ClientSize = clientSize;
-            FrameSize = PlatformImpl!.FrameSize;
             Width = clientSize.Width;
             Height = clientSize.Height;
             LayoutManager.ExecuteLayoutPass();
@@ -789,7 +782,6 @@ namespace Avalonia.Controls
         /// <param name="e">The event args.</param>
         protected virtual void OnOpened(EventArgs e)
         {
-            FrameSize = PlatformImpl?.FrameSize;
             Dispatcher.UIThread.Send(_ => Opened?.Invoke(this, e));
         }
 

+ 11 - 1
src/Avalonia.Controls/WindowBase.cs

@@ -4,6 +4,7 @@ using Avalonia.Controls.Primitives;
 using Avalonia.Input;
 using Avalonia.Layout;
 using Avalonia.Platform;
+using Avalonia.Styling;
 
 namespace Avalonia.Controls
 {
@@ -47,11 +48,18 @@ namespace Avalonia.Controls
         public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current)
         {
             CreatePlatformImplBinding(TopmostProperty, topmost => PlatformImpl!.SetTopmost(topmost));
+            CreatePlatformImplBinding(ActualThemeVariantProperty, variant =>
+            {
+                variant ??= ThemeVariant.Default;
+                PlatformImpl?.SetFrameThemeVariant((PlatformThemeVariant?)variant ?? PlatformThemeVariant.Light);
+            });
+            
+            FrameSize = impl.FrameSize;
         }
 
         public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver? dependencyResolver) : base(impl, dependencyResolver)
         {
-            Screens = new Screens(impl.Screen);
+            Screens = new Screens(impl.Screen!);
             impl.Activated = HandleActivated;
             impl.Deactivated = HandleDeactivated;
             impl.PositionChanged = HandlePositionChanged;
@@ -212,6 +220,8 @@ namespace Avalonia.Controls
         /// <inheritdoc/>
         protected override void OnOpened(EventArgs e)
         {
+            FrameSize = PlatformImpl?.FrameSize;
+            
             // Window must manually raise Loaded/Unloaded events as it is a visual root and
             // does not raise OnAttachedToVisualTreeCore/OnDetachedFromVisualTreeCore events
             ScheduleOnLoadedCore();

+ 1 - 0
src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs

@@ -21,6 +21,7 @@ namespace Avalonia.DesignerSupport.Remote
         public ITrayIconImpl CreateTrayIcon() => null;
 
         public IWindowImpl CreateWindow() => new WindowStub();
+        public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow();
 
         public IWindowImpl CreateEmbeddableWindow()
         {

+ 4 - 4
src/Avalonia.Native/AvaloniaNativeGlPlatformGraphics.cs

@@ -190,10 +190,10 @@ namespace Avalonia.Native
 
     class GlPlatformSurface : IGlPlatformSurface
     {
-        private readonly IAvnWindowBase _window;
-        public GlPlatformSurface(IAvnWindowBase window)
+        private readonly IAvnTopLevel _topLevel;
+        public GlPlatformSurface(IAvnTopLevel topLevel)
         {
-            _window = window;
+            _topLevel = topLevel;
         }
         
         public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(IGlContext context)
@@ -201,7 +201,7 @@ namespace Avalonia.Native
             if (!Dispatcher.UIThread.CheckAccess())
                 throw new RenderTargetNotReadyException();
             var avnContext = (GlContext)context;
-            return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget(avnContext.Context), avnContext);
+            return new GlPlatformSurfaceRenderTarget(_topLevel.CreateGlRenderTarget(avnContext.Context), avnContext);
         }
 
     }

+ 11 - 1
src/Avalonia.Native/AvaloniaNativePlatform.cs

@@ -30,7 +30,12 @@ namespace Avalonia.Native
 
         public static AvaloniaNativePlatform Initialize(IntPtr factory, AvaloniaNativePlatformOptions options)
         {
-            var result = new AvaloniaNativePlatform(MicroComRuntime.CreateProxyFor<IAvaloniaNativeFactory>(factory, true));
+            var factoryProxy = MicroComRuntime.CreateProxyFor<IAvaloniaNativeFactory>(factory, true);
+
+            AvaloniaLocator.CurrentMutable.Bind<IAvaloniaNativeFactory>().ToConstant(factoryProxy);
+            
+            var result = new AvaloniaNativePlatform(factoryProxy);
+            
             result.DoInitialize(options);
 
             return result;
@@ -196,5 +201,10 @@ namespace Avalonia.Native
         {
             throw new NotImplementedException();
         }
+        
+        public ITopLevelImpl CreateEmbeddableTopLevel()
+        {
+            return new EmbeddableTopLevelImpl(_factory);
+        }
     }
 }

+ 2 - 2
src/Avalonia.Native/AvaloniaNativeTextInputMethod.cs

@@ -13,9 +13,9 @@ namespace Avalonia.Native
         private IAvnTextInputMethodClient? _nativeClient;
         private readonly IAvnTextInputMethod _inputMethod;
         
-        public AvaloniaNativeTextInputMethod(IAvnWindowBase nativeWindow)
+        public AvaloniaNativeTextInputMethod(IAvnTopLevel topLevel)
         {
-            _inputMethod = nativeWindow.InputMethod;
+            _inputMethod = topLevel.InputMethod;
         }
 
         public void Dispose()

+ 4 - 4
src/Avalonia.Native/DeferredFramebuffer.cs

@@ -9,13 +9,13 @@ namespace Avalonia.Native
     internal unsafe class DeferredFramebuffer : ILockedFramebuffer
     {
         private readonly IAvnSoftwareRenderTarget _renderTarget;
-        private readonly Action<Action<IAvnWindowBase>> _lockWindow;
+        private readonly Action<Action<IAvnTopLevel>> _lockTopLevel;
         
-        public DeferredFramebuffer(IAvnSoftwareRenderTarget renderTarget, Action<Action<IAvnWindowBase>> lockWindow,
+        public DeferredFramebuffer(IAvnSoftwareRenderTarget renderTarget, Action<Action<IAvnTopLevel>> lockTopLevel,
                                    int width, int height, Vector dpi)
         {
             _renderTarget = renderTarget;
-            _lockWindow = lockWindow;
+            _lockTopLevel = lockTopLevel;
             Address = Marshal.AllocHGlobal(width * height * 4);
             Size = new PixelSize(width, height);
             RowBytes = width * 4;
@@ -35,7 +35,7 @@ namespace Avalonia.Native
             if (Address == IntPtr.Zero)
                 return;
 
-            _lockWindow(win =>
+            _lockTopLevel(win =>
             {
                 var fb = new AvnFramebuffer
                 {

+ 15 - 0
src/Avalonia.Native/EmbeddableTopLevelImpl.cs

@@ -0,0 +1,15 @@
+using Avalonia.Native.Interop;
+
+namespace Avalonia.Native
+{
+    internal class EmbeddableTopLevelImpl : TopLevelImpl
+    {
+        public EmbeddableTopLevelImpl(IAvaloniaNativeFactory factory) : base(factory)
+        {
+            using (var e = new TopLevelEvents(this))
+            {
+                Init(new MacOSTopLevelHandle(factory.CreateTopLevel(e)), factory.CreateScreens());
+            }
+        }
+    }
+}

+ 4 - 4
src/Avalonia.Native/Metal.cs

@@ -50,11 +50,11 @@ class MetalDevice : IMetalDevice
 
 class MetalPlatformSurface : IMetalPlatformSurface
 {
-    private readonly IAvnWindowBase _window;
+    private readonly IAvnTopLevel _topLevel;
 
-    public MetalPlatformSurface(IAvnWindowBase window)
+    public MetalPlatformSurface(IAvnTopLevel topLevel)
     {
-        _window = window;
+        _topLevel = topLevel;
     }
     public IMetalPlatformSurfaceRenderTarget CreateMetalRenderTarget(IMetalDevice device)
     {
@@ -62,7 +62,7 @@ class MetalPlatformSurface : IMetalPlatformSurface
             throw new RenderTargetNotReadyException();
         
         var dev = (MetalDevice)device;
-        var target = _window.CreateMetalRenderTarget(dev.Native);
+        var target = _topLevel.CreateMetalRenderTarget(dev.Native);
         return new MetalRenderTarget(target);
     }
 }

+ 25 - 6
src/Avalonia.Native/PopupImpl.cs

@@ -1,5 +1,4 @@
-using System;
-using Avalonia.Controls;
+using Avalonia.Controls;
 using Avalonia.Controls.Primitives.PopupPositioning;
 using Avalonia.Native.Interop;
 using Avalonia.Platform;
@@ -8,17 +7,37 @@ namespace Avalonia.Native
 {
     class PopupImpl : WindowBaseImpl, IPopupImpl
     {
-        private readonly IWindowBaseImpl _parent;
+        private readonly ITopLevelImpl _parent;
+        private readonly IAvnPopup _native;
+        private readonly AvaloniaNativeTextInputMethod _inputMethod;
 
         public PopupImpl(IAvaloniaNativeFactory factory,
-            IWindowBaseImpl parent) : base(factory)
+            ITopLevelImpl parent) : base(factory)
         {
             _parent = parent;
+            
             using (var e = new PopupEvents(this))
             {
-                Init(factory.CreatePopup(e), factory.CreateScreens());
+                Init(new MacOSTopLevelHandle(_native = factory.CreatePopup(e)), factory.CreateScreens());
             }
+            
             PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));
+
+            while (parent is PopupImpl popupImpl)
+            {
+                parent = popupImpl._parent;
+            }
+
+            //Use the parent's input context to process events
+            if (parent is TopLevelImpl topLevelImpl)
+            {
+                _inputMethod = topLevelImpl.InputMethod;
+            }
+        }
+
+        internal sealed override void Init(MacOSTopLevelHandle handle, IAvnScreens screens)
+        {
+            base.Init(handle, screens);
         }
 
         private void MoveResize(PixelPoint position, Size size, double scaling)
@@ -62,7 +81,7 @@ namespace Avalonia.Native
             base.Show(false, isDialog);
         }
 
-        public override IPopupImpl CreatePopup() => new PopupImpl(_factory, this);
+        public override IPopupImpl CreatePopup() => new PopupImpl(Factory, this);
 
         public void SetWindowManagerAddShadowHint(bool enabled)
         {

+ 6 - 6
src/Avalonia.Native/SystemDialogs.cs

@@ -14,12 +14,12 @@ namespace Avalonia.Native
 {
     internal class SystemDialogs : BclStorageProvider
     {
-        private readonly WindowBaseImpl _window;
+        private readonly TopLevelImpl _topLevel;
         private readonly IAvnSystemDialogs _native;
 
-        public SystemDialogs(WindowBaseImpl window, IAvnSystemDialogs native)
+        public SystemDialogs(TopLevelImpl topLevel, IAvnSystemDialogs native)
         {
-            _window = window;
+            _topLevel = topLevel;
             _native = native;
         }
 
@@ -36,7 +36,7 @@ namespace Avalonia.Native
 
             var suggestedDirectory = options.SuggestedStartLocation?.TryGetLocalPath() ?? string.Empty;
 
-            _native.OpenFileDialog((IAvnWindow)_window.Native,
+            _native.OpenFileDialog((IAvnWindow)_topLevel.Native,
                                     events,
                                     options.AllowMultiple.AsComBool(),
                                     options.Title ?? string.Empty,
@@ -57,7 +57,7 @@ namespace Avalonia.Native
 
             var suggestedDirectory = options.SuggestedStartLocation?.TryGetLocalPath() ?? string.Empty;
 
-            _native.SaveFileDialog((IAvnWindow)_window.Native,
+            _native.SaveFileDialog((IAvnWindow)_topLevel.Native,
                         events,
                         options.Title ?? string.Empty,
                         suggestedDirectory,
@@ -76,7 +76,7 @@ namespace Avalonia.Native
 
             var suggestedDirectory = options.SuggestedStartLocation?.TryGetLocalPath() ?? string.Empty;
 
-            _native.SelectFolderDialog((IAvnWindow)_window.Native, events, options.AllowMultiple.AsComBool(), options.Title ?? "", suggestedDirectory);
+            _native.SelectFolderDialog((IAvnWindow)_topLevel.Native, events, options.AllowMultiple.AsComBool(), options.Title ?? "", suggestedDirectory);
 
             var result = await events.Task.ConfigureAwait(false);
             return result?.Select(f => new BclStorageFolder(new DirectoryInfo(f))).ToArray()

+ 557 - 0
src/Avalonia.Native/TopLevelImpl.cs

@@ -0,0 +1,557 @@
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using Avalonia.Automation.Peers;
+using Avalonia.Controls;
+using Avalonia.Controls.Platform;
+using Avalonia.Controls.Platform.Surfaces;
+using Avalonia.Input;
+using Avalonia.Input.Platform;
+using Avalonia.Input.Raw;
+using Avalonia.Input.TextInput;
+using Avalonia.Native.Interop;
+using Avalonia.Platform;
+using Avalonia.Platform.Storage;
+using Avalonia.Platform.Storage.FileIO;
+using Avalonia.Rendering.Composition;
+using Avalonia.Threading;
+
+namespace Avalonia.Native;
+
+internal class MacOSTopLevelHandle : IPlatformHandle, IMacOSTopLevelPlatformHandle
+{
+    internal MacOSTopLevelHandle(IAvnTopLevel native)
+    {
+        Native = native;
+
+        HandleDescriptor = "NSView";
+
+        Handle = NSView;
+    }
+
+    internal MacOSTopLevelHandle(IAvnWindowBase native)
+    {
+        Native = native;
+
+        HandleDescriptor = "NSWindow";
+
+        Handle = NSWindow;
+    }
+
+    internal IAvnTopLevel Native { get; }
+
+    public IntPtr Handle { get; }
+
+    public string HandleDescriptor { get; }
+
+    public IntPtr NSView => Native.ObtainNSViewHandle();
+
+    public IntPtr GetNSViewRetained()
+    {
+        return Native.ObtainNSViewHandleRetained();
+    }
+    
+    public IntPtr NSWindow => (Native as IAvnWindowBase)?.ObtainNSWindowHandle() ?? IntPtr.Zero;
+
+    public IntPtr GetNSWindowRetained()
+    {
+        return (Native as IAvnWindowBase)?.ObtainNSWindowHandleRetained() ?? IntPtr.Zero;
+    }
+}
+
+internal class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface
+{
+    protected IInputRoot? _inputRoot;
+    private NativeControlHostImpl? _nativeControlHost;
+    private IStorageProvider? _storageProvider;
+    private PlatformBehaviorInhibition? _platformBehaviorInhibition;
+
+    private readonly MouseDevice? _mouse;
+    private readonly IKeyboardDevice? _keyboard;
+    private readonly ICursorFactory? _cursorFactory;
+
+    protected readonly IAvaloniaNativeFactory Factory;
+
+    private Size _savedLogicalSize;
+    private double _savedScaling;
+    private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None;
+
+    protected MacOSTopLevelHandle? _handle;
+
+    private object _syncRoot = new object();
+    private IEnumerable<object>? _surfaces;
+
+    public TopLevelImpl(IAvaloniaNativeFactory factory)
+    {
+        Factory = factory;
+
+        _keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
+        _mouse = new MouseDevice();
+        _cursorFactory = AvaloniaLocator.Current.GetService<ICursorFactory>();
+    }
+
+    internal virtual void Init(MacOSTopLevelHandle handle, IAvnScreens screens)
+    {
+        _handle = handle;
+        _savedLogicalSize = ClientSize;
+        _savedScaling = RenderScaling;
+        _nativeControlHost = new NativeControlHostImpl(Native!.CreateNativeControlHost());
+        _storageProvider = new SystemDialogs(this, Factory.CreateSystemDialogs());
+        _platformBehaviorInhibition = new PlatformBehaviorInhibition(Factory.CreatePlatformBehaviorInhibition());
+        _surfaces = new object[] { new GlPlatformSurface(Native), new MetalPlatformSurface(Native), this };
+        
+        Screen = new ScreenImpl(screens);
+        InputMethod = new AvaloniaNativeTextInputMethod(Native);
+    }
+
+    public double DesktopScaling => 1;
+
+    public IAvnTopLevel? Native => _handle?.Native;
+    public IPlatformHandle? Handle => _handle;
+    public AvaloniaNativeTextInputMethod? InputMethod { get; private set; }
+    public Size ClientSize
+    {
+        get
+        {
+            if (Native == null)
+            {
+                return default;
+            }
+            
+            var s = Native.ClientSize;
+            return new Size(s.Width, s.Height);
+
+        }
+    }
+    public double RenderScaling => Native?.Scaling ?? 1;
+    public IEnumerable<object> Surfaces => _surfaces ?? Array.Empty<object>();
+    public Action<RawInputEventArgs>? Input { get; set; }
+    public Action<Rect>? Paint { get; set; }
+    public Action<Size, WindowResizeReason>? Resized { get; set; }
+    public Action<double>? ScalingChanged { get; set; }
+    public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
+    public Compositor Compositor => AvaloniaNativePlatform.Compositor;
+    public Action? Closed { get; set; }
+    public Action? LostFocus { get; set; }
+    
+    public WindowTransparencyLevel TransparencyLevel
+    {
+        get => _transparencyLevel;
+        private set
+        {
+            if (_transparencyLevel != value)
+            {
+                _transparencyLevel = value;
+                TransparencyLevelChanged?.Invoke(value);
+            }
+        }
+    }
+
+    public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0, 0);
+    public virtual void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+    {
+        //noop
+    }
+
+    public IMouseDevice? MouseDevice => _mouse;
+
+    public INativeControlHostImpl? NativeControlHost => _nativeControlHost;
+
+    public IScreenImpl? Screen { get; private set; }
+
+    public AutomationPeer? GetAutomationPeer()
+    {
+        return _inputRoot is Control c ? ControlAutomationPeer.CreatePeerForElement(c) : null;
+    }
+
+    public bool RawTextInputEvent(ulong timeStamp, string text)
+    {
+        if (_inputRoot is null)
+            return false;
+
+        if (_keyboard is null)
+        {
+            return false;
+        }
+
+        Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
+
+        var args = new RawTextInputEventArgs(_keyboard, timeStamp, _inputRoot, text);
+
+        Input?.Invoke(args);
+
+        return args.Handled;
+    }
+
+    public bool RawKeyEvent(
+        AvnRawKeyEventType type,
+        ulong timeStamp,
+        AvnInputModifiers modifiers,
+        AvnKey key,
+        AvnPhysicalKey physicalKey,
+        string keySymbol)
+    {
+        if (_inputRoot is null)
+            return false;
+
+        if (_keyboard is null)
+        {
+            return false;
+        }
+
+        Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
+
+        var args = new RawKeyEventArgs(
+            _keyboard,
+            timeStamp,
+            _inputRoot,
+            (RawKeyEventType)type,
+            (Key)key,
+            (RawInputModifiers)modifiers,
+            (PhysicalKey)physicalKey,
+            keySymbol);
+
+        Input?.Invoke(args);
+
+        return args.Handled;
+    }
+
+    public void RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+    {
+        if (_inputRoot is null)
+            return;
+
+        if (_mouse is null)
+        {
+            return;
+        }
+
+        Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
+
+        switch (type)
+        {
+            case AvnRawMouseEventType.Wheel:
+                Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot,
+                    point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers));
+                break;
+
+            case AvnRawMouseEventType.Magnify:
+                Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Magnify,
+                    point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers));
+                break;
+
+            case AvnRawMouseEventType.Rotate:
+                Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Rotate,
+                    point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers));
+                break;
+
+            case AvnRawMouseEventType.Swipe:
+                Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Swipe,
+                    point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers));
+                break;
+
+            default:
+                var e = new RawPointerEventArgs(_mouse, timeStamp, _inputRoot, (RawPointerEventType)type,
+                    point.ToAvaloniaPoint(), (RawInputModifiers)modifiers);
+
+                if (!ChromeHitTest(e))
+                {
+                    Input?.Invoke(e);
+                }
+                break;
+        }
+    }
+
+    public void Invalidate()
+    {
+        Native?.Invalidate();
+    }
+
+    public void SetInputRoot(IInputRoot inputRoot)
+    {
+        _inputRoot = inputRoot;
+    }
+
+    public Point PointToClient(PixelPoint point)
+    {
+        return Native?.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint() ?? default;
+    }
+
+    public PixelPoint PointToScreen(Point point)
+    {
+        return Native?.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint() ?? default;
+    }
+
+    public void SetCursor(ICursorImpl? cursor)
+    {
+        if (Native == null)
+        {
+            return;
+        }
+
+        var newCursor = cursor as AvaloniaNativeCursor;
+        newCursor ??= (_cursorFactory?.GetCursor(StandardCursorType.Arrow) as AvaloniaNativeCursor);
+        Native.SetCursor(newCursor?.Cursor);
+    }
+
+    public virtual IPopupImpl CreatePopup()
+    {
+        return new PopupImpl(Factory, this);
+    }
+
+    public void SetTransparencyLevelHint(IReadOnlyList<WindowTransparencyLevel> transparencyLevels)
+    {
+        foreach (var level in transparencyLevels)
+        {
+            AvnWindowTransparencyMode? mode = null;
+
+            if (level == WindowTransparencyLevel.None)
+                mode = AvnWindowTransparencyMode.Opaque;
+            if (level == WindowTransparencyLevel.Transparent)
+                mode = AvnWindowTransparencyMode.Transparent;
+            else if (level == WindowTransparencyLevel.AcrylicBlur)
+                mode = AvnWindowTransparencyMode.Blur;
+
+            if (mode.HasValue && level != TransparencyLevel)
+            {
+                Native?.SetTransparencyMode(mode.Value);
+                TransparencyLevel = level;
+                return;
+            }
+        }
+
+        // If we get here, we didn't find a supported level. Use the default of None.
+        if (TransparencyLevel != WindowTransparencyLevel.None)
+        {
+            Native?.SetTransparencyMode(AvnWindowTransparencyMode.Opaque);
+            TransparencyLevel = WindowTransparencyLevel.None;
+        }
+    }
+
+    public virtual object? TryGetFeature(Type featureType)
+    {
+        if (featureType == typeof(ITextInputMethodImpl))
+        {
+            return InputMethod;
+        }
+
+        if (featureType == typeof(INativeControlHostImpl))
+        {
+            return _nativeControlHost;
+        }
+
+        if (featureType == typeof(IStorageProvider))
+        {
+            return _storageProvider;
+        }
+
+        if (featureType == typeof(IPlatformBehaviorInhibition))
+        {
+            return _platformBehaviorInhibition;
+        }
+
+        if (featureType == typeof(IClipboard))
+        {
+            return AvaloniaLocator.Current.GetRequiredService<IClipboard>();
+        }
+
+        if (featureType == typeof(ILauncher))
+        {
+            return new BclLauncher();
+        }
+
+        return null;
+    }
+
+    public virtual void Dispose()
+    {
+        Native?.Dispose();
+        _handle = null;
+
+        _nativeControlHost?.Dispose();
+        _nativeControlHost = null;
+
+        (Screen as ScreenImpl)?.Dispose();
+        _mouse?.Dispose();
+    }
+
+    protected virtual bool ChromeHitTest(RawPointerEventArgs e)
+    {
+        return false;
+    }
+
+    IFramebufferRenderTarget IFramebufferPlatformSurface.CreateFramebufferRenderTarget()
+    {
+        if (!Dispatcher.UIThread.CheckAccess())
+            throw new RenderTargetNotReadyException();
+
+        var nativeRenderTarget = Native?.CreateSoftwareRenderTarget();
+
+        if (nativeRenderTarget is null)
+        {
+            throw new RenderTargetNotReadyException();
+        }
+        
+        return new FramebufferRenderTarget(this, nativeRenderTarget);
+    }
+
+    protected internal unsafe class TopLevelEvents : NativeCallbackBase, IAvnTopLevelEvents
+    {
+        private readonly TopLevelImpl _parent;
+
+        public TopLevelEvents(TopLevelImpl parent)
+        {
+            _parent = parent;
+        }
+
+        void IAvnTopLevelEvents.Closed()
+        {
+            var n = _parent.Native;
+
+            try
+            {
+                _parent?.Closed?.Invoke();
+            }
+            finally
+            {
+
+                _parent?.Dispose();
+                n?.Dispose();
+            }
+        }
+
+        void IAvnTopLevelEvents.Paint()
+        {
+            Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender);
+            var s = _parent.ClientSize;
+            _parent.Paint?.Invoke(new Rect(0, 0, s.Width, s.Height));
+        }
+
+        void IAvnTopLevelEvents.Resized(AvnSize* size, AvnPlatformResizeReason reason)
+        {
+            if (_parent?.Native == null)
+            {
+                return;
+            }
+            
+            var s = new Size(size->Width, size->Height);
+            _parent._savedLogicalSize = s;
+            _parent.Resized?.Invoke(s, (WindowResizeReason)reason);
+        }
+
+        void IAvnTopLevelEvents.RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+        {
+            _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
+        }
+
+        int IAvnTopLevelEvents.RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnKey key, AvnPhysicalKey physicalKey, string keySymbol)
+        {
+            return _parent.RawKeyEvent(type, timeStamp, modifiers, key, physicalKey, keySymbol).AsComBool();
+        }
+
+        int IAvnTopLevelEvents.RawTextInputEvent(ulong timeStamp, string text)
+        {
+            return _parent.RawTextInputEvent(timeStamp, text).AsComBool();
+        }
+
+        void IAvnTopLevelEvents.ScalingChanged(double scaling)
+        {
+            _parent._savedScaling = scaling;
+            _parent.ScalingChanged?.Invoke(scaling);
+        }
+
+        void IAvnTopLevelEvents.RunRenderPriorityJobs()
+        {
+            Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender);
+        }
+
+        void IAvnTopLevelEvents.LostFocus()
+        {
+            _parent.LostFocus?.Invoke();
+        }
+
+        AvnDragDropEffects IAvnTopLevelEvents.DragEvent(AvnDragEventType type, AvnPoint position,
+            AvnInputModifiers modifiers,
+            AvnDragDropEffects effects,
+            IAvnClipboard clipboard, IntPtr dataObjectHandle)
+        {
+            var device = AvaloniaLocator.Current.GetService<IDragDropDevice>();
+
+            if (device is null)
+            {
+                return AvnDragDropEffects.None;
+            }
+
+            if (_parent._inputRoot is null)
+            {
+                return AvnDragDropEffects.None;
+            }
+            
+            IDataObject? dataObject = null;
+            if (dataObjectHandle != IntPtr.Zero)
+                dataObject = GCHandle.FromIntPtr(dataObjectHandle).Target as IDataObject;
+
+            using (var clipboardDataObject = new ClipboardDataObject(clipboard))
+            {
+                if (dataObject == null)
+                    dataObject = clipboardDataObject;
+
+                var args = new RawDragEvent(device, (RawDragEventType)type,
+                    _parent._inputRoot, position.ToAvaloniaPoint(), dataObject, (DragDropEffects)effects,
+                    (RawInputModifiers)modifiers);
+                _parent.Input(args);
+                return (AvnDragDropEffects)args.Effects;
+            }
+        }
+
+        IAvnAutomationPeer? IAvnTopLevelEvents.AutomationPeer
+        {
+            get
+            {
+                var native = _parent.GetAutomationPeer();
+
+                return native is null ? null : AvnAutomationPeer.Wrap(native);
+            }
+        }
+    }
+
+    private class FramebufferRenderTarget : IFramebufferRenderTarget
+    {
+        private readonly TopLevelImpl _parent;
+        private IAvnSoftwareRenderTarget? _target;
+
+        public FramebufferRenderTarget(TopLevelImpl parent, IAvnSoftwareRenderTarget target)
+        {
+            _parent = parent;
+            _target = target;
+        }
+
+        public void Dispose()
+        {
+            lock (_parent._syncRoot)
+            {
+                _target?.Dispose();
+                _target = null;
+            }
+        }
+
+        public ILockedFramebuffer Lock()
+        {
+            var w = _parent._savedLogicalSize.Width * _parent._savedScaling;
+            var h = _parent._savedLogicalSize.Height * _parent._savedScaling;
+            var dpi = _parent._savedScaling * 96;
+            return new DeferredFramebuffer(_target, cb =>
+            {
+                lock (_parent._syncRoot)
+                {
+                    if (_parent.Native != null && _target != null)
+                    {
+                        cb(_parent.Native);
+                    }
+                }
+            }, (int)w, (int)h, new Vector(dpi, dpi));
+        }
+    }
+}

+ 9 - 7
src/Avalonia.Native/WindowImpl.cs

@@ -16,7 +16,6 @@ namespace Avalonia.Native
         private double _extendTitleBarHeight = -1;
         private DoubleClickHelper _doubleClickHelper;
         private readonly ITopLevelNativeMenuExporter _nativeMenuExporter;
-        private readonly AvaloniaNativeTextInputMethod _inputMethod;
         private bool _canResize = true;
 
         internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(factory)
@@ -26,12 +25,15 @@ namespace Avalonia.Native
             
             using (var e = new WindowEvents(this))
             {
-                Init(_native = factory.CreateWindow(e), factory.CreateScreens());
+                Init(new MacOSTopLevelHandle(_native = factory.CreateWindow(e)), factory.CreateScreens());
             }
 
             _nativeMenuExporter = new AvaloniaNativeMenuExporter(_native, factory);
-            
-            _inputMethod = new AvaloniaNativeTextInputMethod(_native);
+        }
+
+        internal sealed override void Init(MacOSTopLevelHandle handle, IAvnScreens screens)
+        {
+            base.Init(handle, screens);
         }
 
         class WindowEvents : WindowBaseEvents, IAvnWindowEvents
@@ -65,7 +67,7 @@ namespace Avalonia.Native
                 _parent.GotInputWhenDisabled?.Invoke();
             }
         }
-
+        
         public new IAvnWindow Native => _native;
 
         public void CanResize(bool value)
@@ -211,7 +213,7 @@ namespace Avalonia.Native
         public void Move(PixelPoint point) => Position = point;
 
         public override IPopupImpl CreatePopup() =>
-            _opts.OverlayPopups ? null : new PopupImpl(_factory, this);
+            _opts.OverlayPopups ? null : new PopupImpl(Factory, this);
 
         public Action GotInputWhenDisabled { get; set; }
 
@@ -229,7 +231,7 @@ namespace Avalonia.Native
         {
             if(featureType == typeof(ITextInputMethodImpl))
             {
-                return _inputMethod;
+                return InputMethod;
             } 
             
             if (featureType == typeof(ITopLevelNativeMenuExporter))

+ 47 - 499
src/Avalonia.Native/WindowImplBase.cs

@@ -1,123 +1,44 @@
+#nullable enable
+
 using System;
-using System.Collections.Generic;
 using System.Linq;
-using System.Runtime.InteropServices;
-using Avalonia.Automation.Peers;
 using Avalonia.Controls;
-using Avalonia.Controls.Platform;
-using Avalonia.Controls.Platform.Surfaces;
 using Avalonia.Input;
-using Avalonia.Input.Platform;
-using Avalonia.Input.Raw;
 using Avalonia.Native.Interop;
 using Avalonia.Platform;
-using Avalonia.Platform.Storage;
-using Avalonia.Platform.Storage.FileIO;
-using Avalonia.Rendering.Composition;
-using Avalonia.Threading;
 
 namespace Avalonia.Native
 {
-    internal class MacOSTopLevelWindowHandle : IPlatformHandle, IMacOSTopLevelPlatformHandle
+    internal abstract class WindowBaseImpl : TopLevelImpl, IWindowBaseImpl
     {
-        IAvnWindowBase _native;
-
-        public MacOSTopLevelWindowHandle(IAvnWindowBase native)
-        {
-            _native = native;
-        }
-
-        public IntPtr Handle => NSWindow;
-
-        public string HandleDescriptor => "NSWindow";
-
-        public IntPtr NSView => _native?.ObtainNSViewHandle() ?? IntPtr.Zero;
-
-        public IntPtr NSWindow => _native?.ObtainNSWindowHandle() ?? IntPtr.Zero;
-
-        public IntPtr GetNSViewRetained()
+        internal WindowBaseImpl(IAvaloniaNativeFactory factory) : base(factory)
         {
-            return _native?.ObtainNSViewHandleRetained() ?? IntPtr.Zero;
-        }
 
-        public IntPtr GetNSWindowRetained()
-        {
-            return _native?.ObtainNSWindowHandleRetained() ?? IntPtr.Zero;
         }
-    }
 
-    internal abstract class WindowBaseImpl : IWindowBaseImpl,
-        IFramebufferPlatformSurface
-    {
-        protected readonly IAvaloniaNativeFactory _factory;
-        protected IInputRoot _inputRoot;
-        IAvnWindowBase _native;
-        private object _syncRoot = new object();
-        private readonly MouseDevice _mouse;
-        private readonly IKeyboardDevice _keyboard;
-        private readonly ICursorFactory _cursorFactory;
-        private Size _savedLogicalSize;
-        private double _savedScaling;
-        private NativeControlHostImpl _nativeControlHost;
-        private IStorageProvider _storageProvider;
-        private PlatformBehaviorInhibition _platformBehaviorInhibition;
-        private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None;
+        public new IAvnWindowBase? Native => _handle?.Native as IAvnWindowBase;
 
-        internal WindowBaseImpl(IAvaloniaNativeFactory factory)
-        {
-            _factory = factory;
-
-            _keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
-            _mouse = new MouseDevice();
-            _cursorFactory = AvaloniaLocator.Current.GetService<ICursorFactory>();
-        }
-
-        protected void Init(IAvnWindowBase window, IAvnScreens screens)
+        public PixelPoint Position
         {
-            _native = window;
-
-            Surfaces = new object[] { new GlPlatformSurface(window), new MetalPlatformSurface(window), this };
-            Handle = new MacOSTopLevelWindowHandle(window);
-            Screen = new ScreenImpl(screens);
-
-            _savedLogicalSize = ClientSize;
-            _savedScaling = RenderScaling;
-            _nativeControlHost = new NativeControlHostImpl(_native.CreateNativeControlHost());
-            _storageProvider = new SystemDialogs(this, _factory.CreateSystemDialogs());
-            _platformBehaviorInhibition = new PlatformBehaviorInhibition(_factory.CreatePlatformBehaviorInhibition());
-
-            var monitor = Screen.AllScreens.OrderBy(x => x.Scaling)
-                    .FirstOrDefault(m => m.Bounds.Contains(Position));
-
-            Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), WindowResizeReason.Layout);
+            get => Native?.Position.ToAvaloniaPixelPoint() ?? default;
+            set => Native?.SetPosition(value.ToAvnPoint());
         }
 
-        public IAvnWindowBase Native => _native;
+        public Action? Deactivated { get; set; }
+        public Action? Activated { get; set; }
 
-        public Size ClientSize 
-        {
-            get
-            {
-                if (_native != null)
-                {
-                    var s = _native.ClientSize;
-                    return new Size(s.Width, s.Height);
-                }
-
-                return default;
-            }
-        }
+        public Action<PixelPoint>? PositionChanged { get; set; }
 
         public Size? FrameSize
         {
             get
             {
-                if (_native != null)
+                if (Native != null)
                 {
                     unsafe
                     {
                         var s = new AvnSize { Width = -1, Height = -1 };
-                        _native.GetFrameSize(&s);
+                        Native.GetFrameSize(&s);
                         return s.Width < 0  && s.Height < 0 ? null : new Size(s.Width, s.Height);
                     }
                 }
@@ -125,471 +46,98 @@ namespace Avalonia.Native
                 return default;
             }
         }
-
-        public IEnumerable<object> Surfaces { get; private set; }
-
-        public INativeControlHostImpl NativeControlHost => _nativeControlHost;
-
-
-        IFramebufferRenderTarget IFramebufferPlatformSurface.CreateFramebufferRenderTarget()
-        {
-            if (!Dispatcher.UIThread.CheckAccess())
-                throw new RenderTargetNotReadyException();
-            return new FramebufferRenderTarget(this, _native.CreateSoftwareRenderTarget());
-        }
-
-        class FramebufferRenderTarget : IFramebufferRenderTarget
-        {
-            private readonly WindowBaseImpl _parent;
-            private IAvnSoftwareRenderTarget _target;
-
-            public FramebufferRenderTarget(WindowBaseImpl parent, IAvnSoftwareRenderTarget target)
-            {
-                _parent = parent;
-                _target = target;
-            }
-
-            public void Dispose()
-            {
-                lock (_parent._syncRoot)
-                {
-                    _target?.Dispose();
-                    _target = null;
-                }
-            }
-            
-            public ILockedFramebuffer Lock()
-            {
-                var w = _parent._savedLogicalSize.Width * _parent._savedScaling;
-                var h = _parent._savedLogicalSize.Height * _parent._savedScaling;
-                var dpi = _parent._savedScaling * 96;
-                return new DeferredFramebuffer(_target, cb =>
-                {
-                    lock (_parent._syncRoot)
-                    {
-                        if (_parent._native != null && _target != null)
-                        {
-                            cb(_parent._native);
-                        }
-                    }
-                }, (int)w, (int)h, new Vector(dpi, dpi));
-            }
-        }
-
-        public Action LostFocus { get; set; }
         
-        public Action<Rect> Paint { get; set; }
-        public Action<Size, WindowResizeReason> Resized { get; set; }
-        public Action Closed { get; set; }
-        public IMouseDevice MouseDevice => _mouse;
-        public abstract IPopupImpl CreatePopup();
-
-        public AutomationPeer GetAutomationPeer()
-        {
-            return _inputRoot is Control c ? ControlAutomationPeer.CreatePeerForElement(c) : null;
-        }
-
-        protected unsafe class WindowBaseEvents : NativeCallbackBase, IAvnWindowBaseEvents
-        {
-            private readonly WindowBaseImpl _parent;
-
-            public WindowBaseEvents(WindowBaseImpl parent)
-            {
-                _parent = parent;
-            }
-
-            void IAvnWindowBaseEvents.Closed()
-            {
-                var n = _parent._native;
-                try
-                {
-                    _parent?.Closed?.Invoke();
-                }
-                finally
-                {
-                    
-                    _parent?.Dispose();
-                    n?.Dispose();
-                }
-            }
-
-            void IAvnWindowBaseEvents.Activated() => _parent.Activated?.Invoke();
-
-            void IAvnWindowBaseEvents.Deactivated() => _parent.Deactivated?.Invoke();
-
-            void IAvnWindowBaseEvents.Paint()
-            {
-                Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender);
-                var s = _parent.ClientSize;
-                _parent.Paint?.Invoke(new Rect(0, 0, s.Width, s.Height));
-            }
-
-            void IAvnWindowBaseEvents.Resized(AvnSize* size, AvnPlatformResizeReason reason)
-            {
-                if (_parent?._native != null)
-                {
-                    var s = new Size(size->Width, size->Height);
-                    _parent._savedLogicalSize = s;
-                    _parent.Resized?.Invoke(s, (WindowResizeReason)reason);
-                }
-            }
-
-            void IAvnWindowBaseEvents.PositionChanged(AvnPoint position)
-            {
-                _parent.PositionChanged?.Invoke(position.ToAvaloniaPixelPoint());
-            }
-
-            void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
-            {
-                _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
-            }
-
-            int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnKey key, AvnPhysicalKey physicalKey, string keySymbol)
-            {
-                return _parent.RawKeyEvent(type, timeStamp, modifiers, key, physicalKey, keySymbol).AsComBool();
-            }
-
-            int IAvnWindowBaseEvents.RawTextInputEvent(ulong timeStamp, string text)
-            {
-                return _parent.RawTextInputEvent(timeStamp, text).AsComBool();
-            }
-
-            void IAvnWindowBaseEvents.ScalingChanged(double scaling)
-            {
-                _parent._savedScaling = scaling;
-                _parent.ScalingChanged?.Invoke(scaling);
-            }
-
-            void IAvnWindowBaseEvents.RunRenderPriorityJobs()
-            {
-                Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender);
-            }
-            
-            void IAvnWindowBaseEvents.LostFocus()
-            {
-                _parent.LostFocus?.Invoke();
-            }
-
-            public AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position,
-                AvnInputModifiers modifiers,
-                AvnDragDropEffects effects,
-                IAvnClipboard clipboard, IntPtr dataObjectHandle)
-            {
-                var device = AvaloniaLocator.Current.GetService<IDragDropDevice>();
-
-                IDataObject dataObject = null;
-                if (dataObjectHandle != IntPtr.Zero)
-                    dataObject = GCHandle.FromIntPtr(dataObjectHandle).Target as IDataObject;
-                
-                using(var clipboardDataObject = new ClipboardDataObject(clipboard))
-                {
-                    if (dataObject == null)
-                        dataObject = clipboardDataObject;
-                    
-                    var args = new RawDragEvent(device, (RawDragEventType)type,
-                        _parent._inputRoot, position.ToAvaloniaPoint(), dataObject, (DragDropEffects)effects,
-                        (RawInputModifiers)modifiers);
-                    _parent.Input(args);
-                    return (AvnDragDropEffects)args.Effects;
-                }
-            }
-
-            IAvnAutomationPeer IAvnWindowBaseEvents.AutomationPeer
-            {
-                get => AvnAutomationPeer.Wrap(_parent.GetAutomationPeer());
-            }
-        }
-       
-        public void Activate()
+        internal override void Init(MacOSTopLevelHandle handle, IAvnScreens screens)
         {
-            _native?.Activate();
-        }
+            _handle = handle;
 
-        public bool RawTextInputEvent(ulong timeStamp, string text)
-        {
-            if (_inputRoot is null) 
-                return false;
-            
-            Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
+            base.Init(handle, screens);
 
-            var args = new RawTextInputEventArgs(_keyboard, timeStamp, _inputRoot, text);
+            var monitor = Screen!.AllScreens.OrderBy(x => x.Scaling)
+                .FirstOrDefault(m => m.Bounds.Contains(Position));
 
-            Input?.Invoke(args);
-
-            return args.Handled;
-        }
-
-        public bool RawKeyEvent(
-            AvnRawKeyEventType type,
-            ulong timeStamp,
-            AvnInputModifiers modifiers,
-            AvnKey key,
-            AvnPhysicalKey physicalKey,
-            string keySymbol)
-        {
-            if (_inputRoot is null) 
-                return false;
-            
-            Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
-
-            var args = new RawKeyEventArgs(
-                _keyboard,
-                timeStamp,
-                _inputRoot,
-                (RawKeyEventType)type,
-                (Key)key,
-                (RawInputModifiers)modifiers,
-                (PhysicalKey)physicalKey,
-                keySymbol);
-
-            Input?.Invoke(args);
-
-            return args.Handled;
+            Resize(new Size(monitor!.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), WindowResizeReason.Layout);
         }
 
-        protected virtual bool ChromeHitTest(RawPointerEventArgs e)
+        public void Activate()
         {
-            return false;
-        }
-
-        public void RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
-        {
-            if (_inputRoot is null) 
-                return;
-            
-            Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
-
-            switch (type)
-            {
-                case AvnRawMouseEventType.Wheel:
-                    Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot,
-                        point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers));
-                    break;
-                
-                case AvnRawMouseEventType.Magnify:
-                    Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Magnify, 
-                        point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers));
-                    break;
-                    
-                case AvnRawMouseEventType.Rotate:
-                    Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Rotate, 
-                        point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers));
-                    break;
-                
-                case AvnRawMouseEventType.Swipe:
-                    Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Swipe,
-                        point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers));
-                    break;
-
-                default:
-                    var e = new RawPointerEventArgs(_mouse, timeStamp, _inputRoot, (RawPointerEventType)type,
-                        point.ToAvaloniaPoint(), (RawInputModifiers)modifiers);
-                    
-                    if(!ChromeHitTest(e))
-                    {
-                        Input?.Invoke(e);
-                    }
-                    break;
-            }
+            Native?.Activate();
         }
 
         public void Resize(Size clientSize, WindowResizeReason reason)
         {
-            _native?.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason);
+            Native?.Resize(clientSize.Width, clientSize.Height, (AvnPlatformResizeReason)reason);
         }
-
-        public Compositor Compositor => AvaloniaNativePlatform.Compositor;
-
-        public virtual void Dispose()
-        {
-            _native?.Close();
-            _native?.Dispose();
-            _native = null;
-
-            _nativeControlHost?.Dispose();
-            _nativeControlHost = null;
-            
-            (Screen as ScreenImpl)?.Dispose();
-            _mouse.Dispose();
-        }
-
-
-        public void Invalidate(Rect rect)
+        
+        public override void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
         {
-            _native?.Invalidate(new AvnRect { Height = rect.Height, Width = rect.Width, X = rect.X, Y = rect.Y });
+            Native?.SetFrameThemeVariant((AvnPlatformThemeVariant)themeVariant);
         }
 
-        public void SetInputRoot(IInputRoot inputRoot)
+        public override void Dispose()
         {
-            _inputRoot = inputRoot;
+            Native?.Close();
+            Native?.Dispose();
+            _handle = null;
         }
 
-
         public virtual void Show(bool activate, bool isDialog)
         {
-            _native?.Show(activate.AsComBool(), isDialog.AsComBool());
-        }
-
-
-        public PixelPoint Position
-        {
-            get => _native?.Position.ToAvaloniaPixelPoint() ?? default;
-            set => _native?.SetPosition(value.ToAvnPoint());
-        }
-
-        public Point PointToClient(PixelPoint point)
-        {
-            return _native?.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint() ?? default;
-        }
-
-        public PixelPoint PointToScreen(Point point)
-        {
-            return _native?.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint() ?? default;
+            Native?.Show(activate.AsComBool(), isDialog.AsComBool());
         }
 
         public void Hide()
         {
-            _native?.Hide();
+            Native?.Hide();
         }
 
         public void BeginMoveDrag(PointerPressedEventArgs e)
         {
-            _native?.BeginMoveDrag();
+            Native?.BeginMoveDrag();
         }
 
-        public Size MaxAutoSizeHint => Screen.AllScreens.Select(s => s.Bounds.Size.ToSize(1))
+        public Size MaxAutoSizeHint => Screen!.AllScreens.Select(s => s.Bounds.Size.ToSize(1))
             .OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
 
         public void SetTopmost(bool value)
         {
-            _native?.SetTopMost(value.AsComBool());
-        }
-
-        public double RenderScaling => _native?.Scaling ?? 1;
-
-        public double DesktopScaling => 1;
-
-        public Action Deactivated { get; set; }
-        public Action Activated { get; set; }
-
-        public void SetCursor(ICursorImpl cursor)
-        {
-            if (_native == null)
-            {
-                return;
-            }
-            
-            var newCursor = cursor as AvaloniaNativeCursor;
-            newCursor = newCursor ?? (_cursorFactory.GetCursor(StandardCursorType.Arrow) as AvaloniaNativeCursor);
-            _native.SetCursor(newCursor.Cursor);
+            Native?.SetTopMost(value.AsComBool());
         }
 
-        public Action<PixelPoint> PositionChanged { get; set; }
-
-        public Action<RawInputEventArgs> Input { get; set; }
-
-        public Action<double> ScalingChanged { get; set; }
-
-        public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
-
-        public IScreenImpl Screen { get; private set; }
-
         // TODO
-
-        public void SetMinMaxSize(Size minSize, Size maxSize)
-        {
-            _native?.SetMinMaxSize(minSize.ToAvnSize(), maxSize.ToAvnSize());
-        }
-
         public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e)
         {
 
         }
 
-        internal void BeginDraggingSession(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard clipboard,
-            IAvnDndResultCallback callback, IntPtr sourceHandle)
-        {
-            _native?.BeginDragAndDropOperation(effects, point, clipboard, callback, sourceHandle);
-        }
-
-        public void SetTransparencyLevelHint(IReadOnlyList<WindowTransparencyLevel> transparencyLevels) 
-        {
-            foreach (var level in transparencyLevels)
-            {
-                AvnWindowTransparencyMode? mode = null;
-
-                if (level == WindowTransparencyLevel.None)
-                    mode = AvnWindowTransparencyMode.Opaque;
-                if (level == WindowTransparencyLevel.Transparent)
-                    mode = AvnWindowTransparencyMode.Transparent;
-                else if (level == WindowTransparencyLevel.AcrylicBlur)
-                    mode = AvnWindowTransparencyMode.Blur;
-
-                if (mode.HasValue && level != TransparencyLevel)
-                {
-                    _native?.SetTransparencyMode(mode.Value);
-                    TransparencyLevel = level;
-                    return;
-                }
-            }
-
-            // If we get here, we didn't find a supported level. Use the default of None.
-            if (TransparencyLevel != WindowTransparencyLevel.None)
-            {
-                _native?.SetTransparencyMode(AvnWindowTransparencyMode.Opaque);
-                TransparencyLevel = WindowTransparencyLevel.None;
-            }
-        }
-
-        public WindowTransparencyLevel TransparencyLevel
+        public void SetMinMaxSize(Size minSize, Size maxSize)
         {
-            get => _transparencyLevel;
-            private set
-            {
-                if (_transparencyLevel != value)
-                {
-                    _transparencyLevel = value;
-                    TransparencyLevelChanged?.Invoke(value);
-                }
-            }
+            Native?.SetMinMaxSize(minSize.ToAvnSize(), maxSize.ToAvnSize());
         }
 
-        public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+        internal void BeginDraggingSession(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard clipboard,
+            IAvnDndResultCallback callback, IntPtr sourceHandle)
         {
-            _native.SetFrameThemeVariant((AvnPlatformThemeVariant)themeVariant);
+            Native?.BeginDragAndDropOperation(effects, point, clipboard, callback, sourceHandle);
         }
 
-        public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0, 0);
-        public virtual object TryGetFeature(Type featureType)
+        protected class WindowBaseEvents : TopLevelEvents, IAvnWindowBaseEvents
         {
-            if (featureType == typeof(INativeControlHostImpl))
-            {
-                return _nativeControlHost;
-            }
-            
-            if (featureType == typeof(IStorageProvider))
-            {
-                return _storageProvider;
-            }
+            private readonly WindowBaseImpl _parent;
 
-            if (featureType == typeof(IPlatformBehaviorInhibition))
+            public WindowBaseEvents(WindowBaseImpl parent) : base(parent)
             {
-                return _platformBehaviorInhibition;
+                _parent = parent;
             }
 
-            if (featureType == typeof(IClipboard))
+            void IAvnWindowBaseEvents.PositionChanged(AvnPoint position)
             {
-                return AvaloniaLocator.Current.GetRequiredService<IClipboard>();
+                _parent.PositionChanged?.Invoke(position.ToAvaloniaPixelPoint());
             }
 
-            if (featureType == typeof(ILauncher))
-            {
-                return new BclLauncher();
-            }
+            void IAvnWindowBaseEvents.Activated() => _parent.Activated?.Invoke();
 
-            return null;
+            void IAvnWindowBaseEvents.Deactivated() => _parent.Deactivated?.Invoke();
         }
-
-        public IPlatformHandle Handle { get; private set; }
     }
 }

+ 60 - 39
src/Avalonia.Native/avn.idl

@@ -663,6 +663,7 @@ interface IAvaloniaNativeFactory : IUnknown
      HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator, IAvnApplicationEvents* appCb,
         IAvnDispatcher* dispatcher);
      IAvnMacOptions* GetMacOptions();
+     HRESULT CreateTopLevel(IAvnTopLevelEvents* cb, IAvnTopLevel** ppv);
      HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv);
      HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnPopup** ppv);
      HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv);
@@ -692,41 +693,53 @@ interface IAvnString : IUnknown
      HRESULT Length(int*ret);
 }
 
-[uuid(e5aca675-02b7-4129-aa79-d6e417210bda)]
-interface IAvnWindowBase : IUnknown
+[uuid(e8cccd3e-e6dc-430a-a0b9-2ce7d7922de6)]
+interface IAvnTopLevel : IUnknown
 {
+     HRESULT GetClientSize(AvnSize*ret);
+     HRESULT GetScaling(double*ret);
+     
+     HRESULT Invalidate();
+     
+     HRESULT PointToClient(AvnPoint point, AvnPoint*ret);
+     HRESULT PointToScreen(AvnPoint point, AvnPoint*ret);
+     
+     HRESULT SetCursor(IAvnCursor* cursor);
+     HRESULT CreateGlRenderTarget(IAvnGlContext* context, IAvnGlSurfaceRenderTarget** ret);
+     HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget** ret);
+     HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget** ret);
+     
+     HRESULT ObtainNSViewHandle([intptr]void** retOut);
+     HRESULT ObtainNSViewHandleRetained([intptr]void** retOut);
+     
+     HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut);
+     
+     HRESULT GetInputMethod(IAvnTextInputMethod **ppv);
+     HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode);
+}
+
+[uuid(e5aca675-02b7-4129-aa79-d6e417210bda), cpp-virtual-inherits]
+interface IAvnWindowBase : IAvnTopLevel
+{
+     HRESULT GetFrameSize(AvnSize*result);
+     HRESULT SetFrameThemeVariant(AvnPlatformThemeVariant mode);
+     HRESULT SetParent(IAvnWindowBase* parent);
      HRESULT Show(bool activate, bool isDialog);
      HRESULT Hide();
      HRESULT Close();
-     HRESULT Activate();
-     HRESULT GetClientSize(AvnSize*ret);
-     HRESULT GetFrameSize(AvnSize*result);
-     HRESULT GetScaling(double*ret);
+     HRESULT Activate(); 
      HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize);
      HRESULT Resize(double width, double height, AvnPlatformResizeReason reason);
-     HRESULT Invalidate(AvnRect rect);
      HRESULT BeginMoveDrag();
      HRESULT BeginResizeDrag(AvnWindowEdge edge);
      HRESULT GetPosition(AvnPoint*ret);
      HRESULT SetPosition(AvnPoint point);
-     HRESULT PointToClient(AvnPoint point, AvnPoint*ret);
-     HRESULT PointToScreen(AvnPoint point, AvnPoint*ret);
      HRESULT SetTopMost(bool value);
-     HRESULT SetCursor(IAvnCursor* cursor);
-     HRESULT CreateGlRenderTarget(IAvnGlContext* context, IAvnGlSurfaceRenderTarget** ret);
-     HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget** ret);
-     HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget** ret);
      HRESULT SetMainMenu(IAvnMenu* menu);
      HRESULT ObtainNSWindowHandle([intptr]void** retOut);
      HRESULT ObtainNSWindowHandleRetained([intptr]void** retOut);
-     HRESULT ObtainNSViewHandle([intptr]void** retOut);
-     HRESULT ObtainNSViewHandleRetained([intptr]void** retOut);
-     HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut);
      HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
                                               IAvnClipboard* clipboard, IAvnDndResultCallback* cb, [intptr]void* sourceHandle);
-     HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode);
-     HRESULT SetFrameThemeVariant(AvnPlatformThemeVariant mode);
-     HRESULT GetInputMethod(IAvnTextInputMethod **ppv);
 }
 
 [uuid(83e588f3-6981-4e48-9ea0-e1e569f79a91), cpp-virtual-inherits]
@@ -739,7 +752,6 @@ interface IAvnPopup : IAvnWindowBase
 interface IAvnWindow : IAvnWindowBase
 {
      HRESULT SetEnabled(bool enable);
-     HRESULT SetParent(IAvnWindow* parent);
      HRESULT SetCanResize(bool value);
      HRESULT SetDecorations(SystemDecorations value);
      HRESULT SetTitle(char* utf8Title);
@@ -754,30 +766,39 @@ interface IAvnWindow : IAvnWindowBase
      HRESULT GetWindowZOrder(long*ret);
 }
 
+[uuid(fda9c1b3-69e0-43d7-9459-8cc97cb41f6a)]
+interface IAvnTopLevelEvents : IUnknown
+{
+    void Closed();
+
+    HRESULT Paint();
+    void Resized([const] AvnSize& size, AvnPlatformResizeReason reason);
+    void RawMouseEvent(AvnRawMouseEventType type,
+                                    u_int64_t timeStamp,
+                                    AvnInputModifiers modifiers,
+                                    AvnPoint point,
+                                    AvnVector delta);
+    bool RawKeyEvent(AvnRawKeyEventType type, u_int64_t timeStamp, AvnInputModifiers modifiers,
+                          AvnKey key, AvnPhysicalKey physicalKey, [const] char* keySymbol);
+    bool RawTextInputEvent(u_int64_t timeStamp, [const] char* text);
+    void ScalingChanged(double scaling);
+    void RunRenderPriorityJobs();
+    void LostFocus();
+    
+    IAvnAutomationPeer* GetAutomationPeer();
+    
+    
+    AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position,
+                                         AvnInputModifiers modifiers, AvnDragDropEffects effects,
+                                         IAvnClipboard* clipboard, [intptr]void* dataObjectHandle);
+}
+
 [uuid(939b6599-40a8-4710-a4c8-5d72d8f174fb)]
-interface IAvnWindowBaseEvents : IUnknown
+interface IAvnWindowBaseEvents : IAvnTopLevelEvents
 {
-     HRESULT Paint();
-     void Closed();
      void Activated();
      void Deactivated();
-     void Resized([const] AvnSize& size, AvnPlatformResizeReason reason);
      void PositionChanged(AvnPoint position);
-     void RawMouseEvent(AvnRawMouseEventType type,
-                                u_int64_t timeStamp,
-                                AvnInputModifiers modifiers,
-                                AvnPoint point,
-                                AvnVector delta);
-     bool RawKeyEvent(AvnRawKeyEventType type, u_int64_t timeStamp, AvnInputModifiers modifiers,
-                      AvnKey key, AvnPhysicalKey physicalKey, [const] char* keySymbol);
-     bool RawTextInputEvent(u_int64_t timeStamp, [const] char* text);
-     void ScalingChanged(double scaling);
-     void RunRenderPriorityJobs();
-     void LostFocus();
-     AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position,
-                                         AvnInputModifiers modifiers, AvnDragDropEffects effects,
-                                         IAvnClipboard* clipboard, [intptr]void* dataObjectHandle);
-     IAvnAutomationPeer* GetAutomationPeer();
 }
 
 [uuid(1ae178ee-1fcc-447f-b6dd-b7bb727f934c)]

+ 2 - 0
src/Avalonia.X11/X11Platform.cs

@@ -135,6 +135,8 @@ namespace Avalonia.X11
             return new X11Window(this, null);
         }
 
+        public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow();
+
         public IWindowImpl CreateEmbeddableWindow()
         {
             throw new NotSupportedException();

+ 3 - 0
src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs

@@ -124,6 +124,9 @@ namespace Avalonia.Browser
         {
         }
 
+        public double DesktopScaling => RenderScaling;
+        public IScreenImpl? Screen { get; }
+        public IPlatformHandle? Handle { get; }
         public Size ClientSize => _surface?.ClientSize ?? new Size(1, 1);
         public Size? FrameSize => null;
         public double RenderScaling => _surface?.Scaling ?? 1;

+ 6 - 0
src/Browser/Avalonia.Browser/WindowingPlatform.cs

@@ -58,11 +58,17 @@ internal class BrowserWindowingPlatform : IWindowingPlatform
 
     public IWindowImpl CreateWindow() => throw new NotSupportedException("Browser doesn't support windowing platform. In order to display a single-view content, set ISingleViewApplicationLifetime.MainView.");
 
+
     IWindowImpl IWindowingPlatform.CreateEmbeddableWindow()
     {
         throw new NotImplementedException("Browser doesn't support embeddable windowing platform.");
     }
 
+    ITopLevelImpl IWindowingPlatform.CreateEmbeddableTopLevel()
+    {
+        throw new NotImplementedException();
+    }
+
     public ITrayIconImpl? CreateTrayIcon()
     {
         return null;

+ 1 - 0
src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs

@@ -58,6 +58,7 @@ namespace Avalonia.Headless
                 _frameBufferFormat = frameBufferFormat;
             }
             public IWindowImpl CreateWindow() => new HeadlessWindowImpl(false, _frameBufferFormat);
+            public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow();
 
             public IWindowImpl CreateEmbeddableWindow() => throw new PlatformNotSupportedException();
 

+ 3 - 0
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@@ -53,6 +53,9 @@ using Avalonia.Rendering.Composition;
         {
         }
 
+        public double DesktopScaling => 1;
+        public IScreenImpl Screen { get; }
+        public IPlatformHandle Handle { get; }
         public Size ClientSize => ScaledSize;
         public Size? FrameSize => null;
         public IMouseDevice MouseDevice => new MouseDevice();

+ 2 - 0
src/Tizen/Avalonia.Tizen/Stubs.cs

@@ -9,6 +9,8 @@ internal class WindowingPlatformStub : IWindowingPlatform
 
     public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException();
 
+    public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow();
+
     public ITrayIconImpl? CreateTrayIcon() => null;
 }
 

+ 4 - 0
src/Tizen/Avalonia.Tizen/TopLevelImpl.cs

@@ -26,6 +26,10 @@ internal class TopLevelImpl : ITopLevelImpl
         _clipboard = new NuiClipboardImpl();
     }
 
+    public double DesktopScaling => RenderScaling;
+    public IScreenImpl? Screen { get; }
+    public IPlatformHandle? Handle { get; }
+
     public Size ClientSize => _view.ClientSize;
 
     public Size? FrameSize => null;

+ 1 - 1
src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs

@@ -54,7 +54,7 @@ namespace Avalonia.Win32.Automation
         {
             get
             {
-                var handle = WindowImpl?.Handle.Handle ?? IntPtr.Zero;
+                var handle = WindowImpl?.Handle?.Handle ?? IntPtr.Zero;
                 if (handle == IntPtr.Zero)
                     return null;
                 var hr = UiaCoreProviderApi.UiaHostProviderFromHwnd(handle, out var result);

+ 2 - 2
src/Windows/Avalonia.Win32/PopupImpl.cs

@@ -39,7 +39,7 @@ namespace Avalonia.Win32
             var focusOwner = UnmanagedMethods.GetFocus();
             if (focusOwner != IntPtr.Zero &&
                 UnmanagedMethods.GetAncestor(focusOwner, UnmanagedMethods.GetAncestorFlags.GA_ROOT)
-                == parent.Handle.Handle)
+                == parent.Handle?.Handle)
                 UnmanagedMethods.SetFocus(parent.Handle.Handle);
         }
 
@@ -116,7 +116,7 @@ namespace Avalonia.Win32
         // One fabulous design decision leads to another, I guess
         private static IWindowBaseImpl SaveParentHandle(IWindowBaseImpl parent)
         {
-            s_parentHandle = parent.Handle.Handle;
+            s_parentHandle = parent.Handle!.Handle;
             return parent;
         }
 

+ 7 - 2
src/Windows/Avalonia.Win32/ScreenImpl.cs

@@ -78,9 +78,14 @@ namespace Avalonia.Win32
         /// <inheritdoc />
         public Screen? ScreenFromWindow(IWindowBaseImpl window)
         {
-            var handle = window.Handle.Handle;
+            var handle = window.Handle?.Handle;
 
-            var monitor = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONULL);
+            if (handle is null)
+            {
+                return null;
+            }
+            
+            var monitor = MonitorFromWindow(handle.Value, MONITOR.MONITOR_DEFAULTTONULL);
 
             return FindScreenByHandle(monitor);
         }

+ 2 - 0
src/Windows/Avalonia.Win32/Win32Platform.cs

@@ -217,6 +217,8 @@ namespace Avalonia.Win32
             return new WindowImpl();
         }
 
+        public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow();
+
         public IWindowImpl CreateEmbeddableWindow()
         {
             var embedded = new EmbeddedWindowImpl();

+ 1 - 1
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -256,7 +256,7 @@ namespace Avalonia.Win32
             }
         }
 
-        Size? ITopLevelImpl.FrameSize => FrameSize;
+        Size? IWindowBaseImpl.FrameSize => FrameSize;
 
         public Size FrameSize
         {

+ 3 - 0
src/iOS/Avalonia.iOS/AvaloniaView.cs

@@ -221,6 +221,9 @@ namespace Avalonia.iOS
                 // No-op
             }
 
+            public double DesktopScaling => RenderScaling;
+            public IScreenImpl? Screen { get; }
+            public IPlatformHandle? Handle { get; }
             public Size ClientSize => new Size(_view.Bounds.Width, _view.Bounds.Height);
             public Size? FrameSize => null;
             public double RenderScaling => _view.ContentScaleFactor;

+ 1 - 0
src/iOS/Avalonia.iOS/Stubs.cs

@@ -20,6 +20,7 @@ namespace Avalonia.iOS
     internal class WindowingPlatformStub : IWindowingPlatform
     {
         public IWindowImpl CreateWindow() => throw new NotSupportedException();
+        public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow();
 
         public IWindowImpl CreateEmbeddableWindow() => throw new NotSupportedException();
 

+ 3 - 0
tests/Avalonia.UnitTests/CompositorTestServices.cs

@@ -158,6 +158,9 @@ public class CompositorTestServices : IDisposable
 
         }
 
+        public double DesktopScaling => 1;
+        public IScreenImpl Screen { get; }
+        public IPlatformHandle Handle { get; }
         public Size ClientSize { get; }
         public Size? FrameSize { get; }
         public double RenderScaling => 1;

+ 2 - 0
tests/Avalonia.UnitTests/MockWindowingPlatform.cs

@@ -152,6 +152,8 @@ namespace Avalonia.UnitTests
             }
         }
 
+        public ITopLevelImpl CreateEmbeddableTopLevel() => CreateEmbeddableWindow();
+
         public IWindowImpl CreateEmbeddableWindow()
         {
             throw new NotImplementedException();