Browse Source

Merge branch 'master' into features/NetAnalyzers/CA1822

Giuseppe Lippolis 2 years ago
parent
commit
9fbd163b33
100 changed files with 912 additions and 408 deletions
  1. 5 5
      .gitignore
  2. 0 0
      .ncrunch/Avalonia.Benchmarks.v3.ncrunchproject
  3. 0 0
      .ncrunch/Avalonia.Browser.Blazor.v3.ncrunchproject
  4. 0 0
      .ncrunch/Avalonia.Browser.v3.ncrunchproject
  5. 0 0
      .ncrunch/Avalonia.Designer.HostApp.v3.ncrunchproject
  6. 5 0
      .ncrunch/Avalonia.Themes.Fluent.net6.0.v3.ncrunchproject
  7. 5 0
      .ncrunch/Avalonia.Themes.Fluent.netstandard2.0.v3.ncrunchproject
  8. 5 0
      .ncrunch/Avalonia.Themes.Simple.net6.0.v3.ncrunchproject
  9. 5 0
      .ncrunch/Avalonia.Themes.Simple.netstandard2.0.v3.ncrunchproject
  10. 5 0
      .ncrunch/ControlCatalog.Browser.Blazor.v3.ncrunchproject
  11. 5 0
      .ncrunch/ControlCatalog.Browser.v3.ncrunchproject
  12. 5 0
      .ncrunch/MobileSandbox.v3.ncrunchproject
  13. 27 27
      Avalonia.sln
  14. 0 2
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  15. 1 1
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  16. 3 2
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  17. 2 2
      packages/Avalonia/AvaloniaBuildTasks.targets
  18. 10 4
      samples/BindingDemo/App.xaml
  19. 0 0
      samples/ControlCatalog.Browser.Blazor/App.razor
  20. 2 2
      samples/ControlCatalog.Browser.Blazor/App.razor.cs
  21. 3 3
      samples/ControlCatalog.Browser.Blazor/ControlCatalog.Browser.Blazor.csproj
  22. 1 1
      samples/ControlCatalog.Browser.Blazor/Pages/Index.razor
  23. 1 1
      samples/ControlCatalog.Browser.Blazor/Program.cs
  24. 0 0
      samples/ControlCatalog.Browser.Blazor/Properties/launchSettings.json
  25. 0 0
      samples/ControlCatalog.Browser.Blazor/Shared/MainLayout.razor
  26. 1 1
      samples/ControlCatalog.Browser.Blazor/_Imports.razor
  27. 0 0
      samples/ControlCatalog.Browser.Blazor/wwwroot/css/app.css
  28. 0 0
      samples/ControlCatalog.Browser.Blazor/wwwroot/favicon.ico
  29. 0 0
      samples/ControlCatalog.Browser.Blazor/wwwroot/index.html
  30. 3 3
      samples/ControlCatalog.Browser/ControlCatalog.Browser.csproj
  31. 2 2
      samples/ControlCatalog.Browser/EmbedSample.Browser.cs
  32. 0 0
      samples/ControlCatalog.Browser/Logo.svg
  33. 2 2
      samples/ControlCatalog.Browser/Program.cs
  34. 0 0
      samples/ControlCatalog.Browser/Roots.xml
  35. 0 0
      samples/ControlCatalog.Browser/app.css
  36. 0 0
      samples/ControlCatalog.Browser/embed.js
  37. 0 0
      samples/ControlCatalog.Browser/favicon.ico
  38. 0 0
      samples/ControlCatalog.Browser/index.html
  39. 0 0
      samples/ControlCatalog.Browser/main.js
  40. 0 0
      samples/ControlCatalog.Browser/runtimeconfig.template.json
  41. 9 0
      samples/ControlCatalog.NetCore/Program.cs
  42. 11 0
      samples/ControlCatalog.NetCore/Properties/launchSettings.json
  43. 10 0
      samples/ControlCatalog/ControlCatalog.csproj
  44. 29 0
      samples/ControlCatalog/Converter/DegToRadConverter.cs
  45. 3 0
      samples/ControlCatalog/MainView.xaml
  46. 1 1
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  47. 107 0
      samples/ControlCatalog/Pages/CustomDrawing.xaml
  48. 67 0
      samples/ControlCatalog/Pages/CustomDrawing.xaml.cs
  49. 215 0
      samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
  50. 33 18
      samples/ControlCatalog/Pages/DataGridPage.xaml
  51. 18 0
      samples/ControlCatalog/Pages/ExpanderPage.xaml
  52. 6 0
      samples/ControlCatalog/Pages/ExpanderPage.xaml.cs
  53. 1 0
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  54. 1 7
      samples/ControlCatalog/Pages/TabControlPage.xaml
  55. 3 3
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  56. 31 5
      samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs
  57. 3 3
      samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs
  58. 4 0
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  59. 25 0
      src/Android/Avalonia.Android/AvaloniaMainActivity.cs
  60. 22 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  61. 1 1
      src/Avalonia.Base/Animation/PageSlide.cs
  62. 1 1
      src/Avalonia.Base/AttachedProperty.cs
  63. 0 1
      src/Avalonia.Base/Avalonia.Base.csproj
  64. 1 1
      src/Avalonia.Base/AvaloniaObject.cs
  65. 38 62
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  66. 12 12
      src/Avalonia.Base/AvaloniaProperty.cs
  67. 3 3
      src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs
  68. 2 2
      src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs
  69. 6 6
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  70. 1 1
      src/Avalonia.Base/AvaloniaProperty`1.cs
  71. 2 2
      src/Avalonia.Base/ClassBindingManager.cs
  72. 1 1
      src/Avalonia.Base/Controls/Classes.cs
  73. 1 1
      src/Avalonia.Base/Controls/ISetInheritanceParent.cs
  74. 2 1
      src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
  75. 2 2
      src/Avalonia.Base/Data/BindingOperations.cs
  76. 13 9
      src/Avalonia.Base/Data/BindingPriority.cs
  77. 2 2
      src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs
  78. 1 1
      src/Avalonia.Base/Data/IBinding.cs
  79. 3 3
      src/Avalonia.Base/Data/IndexerBinding.cs
  80. 1 1
      src/Avalonia.Base/Data/InstancedBinding.cs
  81. 1 1
      src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs
  82. 5 5
      src/Avalonia.Base/DirectProperty.cs
  83. 3 3
      src/Avalonia.Base/DirectPropertyBase.cs
  84. 0 79
      src/Avalonia.Base/IAvaloniaObject.cs
  85. 1 1
      src/Avalonia.Base/IDataContextProvider.cs
  86. 2 2
      src/Avalonia.Base/IDirectPropertyAccessor.cs
  87. 0 39
      src/Avalonia.Base/IStyledElement.cs
  88. 2 2
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  89. 3 2
      src/Avalonia.Base/Input/DragDropDevice.cs
  90. 1 1
      src/Avalonia.Base/Input/DragEventArgs.cs
  91. 10 7
      src/Avalonia.Base/Input/FocusManager.cs
  92. 2 2
      src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs
  93. 2 2
      src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
  94. 18 15
      src/Avalonia.Base/Input/Gestures.cs
  95. 34 5
      src/Avalonia.Base/Input/IInputElement.cs
  96. 1 1
      src/Avalonia.Base/Input/IMainMenu.cs
  97. 9 8
      src/Avalonia.Base/Input/InputExtensions.cs
  98. 24 19
      src/Avalonia.Base/Input/KeyboardDevice.cs
  99. 1 1
      src/Avalonia.Base/Input/KeyboardNavigation.cs
  100. 3 3
      src/Avalonia.Base/Input/KeyboardNavigationHandler.cs

+ 5 - 5
.gitignore

@@ -210,9 +210,9 @@ coc-settings.json
 .ccls-cache
 .ccls
 *.map
-src/Web/Avalonia.Web.Blazor/wwwroot/*.js
-src/Web/Avalonia.Web.Blazor/Interop/Typescript/*.js
+src/Browser/Avalonia.Browser.Blazor/wwwroot/*.js
+src/Browser/Avalonia.Browser.Blazor/Interop/Typescript/*.js
 node_modules
-src/Web/Avalonia.Web.Blazor/webapp/package-lock.json
-src/Web/Avalonia.Web.Blazor/wwwroot
-src/Web/Avalonia.Web/wwwroot
+src/Browser/Avalonia.Browser.Blazor/webapp/package-lock.json
+src/Browser/Avalonia.Browser.Blazor/wwwroot
+src/Browser/Avalonia.Browser/wwwroot

+ 0 - 0
.ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject → .ncrunch/Avalonia.Benchmarks.v3.ncrunchproject


+ 0 - 0
.ncrunch/Avalonia.Web.v3.ncrunchproject → .ncrunch/Avalonia.Browser.Blazor.v3.ncrunchproject


+ 0 - 0
.ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject → .ncrunch/Avalonia.Browser.v3.ncrunchproject


+ 0 - 0
.ncrunch/ControlCatalog.Web.v3.ncrunchproject → .ncrunch/Avalonia.Designer.HostApp.v3.ncrunchproject


+ 5 - 0
.ncrunch/Avalonia.Themes.Fluent.net6.0.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <InstrumentOutputAssembly>False</InstrumentOutputAssembly>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Themes.Fluent.netstandard2.0.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <InstrumentOutputAssembly>False</InstrumentOutputAssembly>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Themes.Simple.net6.0.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <InstrumentOutputAssembly>False</InstrumentOutputAssembly>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/Avalonia.Themes.Simple.netstandard2.0.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <InstrumentOutputAssembly>False</InstrumentOutputAssembly>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/ControlCatalog.Browser.Blazor.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/ControlCatalog.Browser.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
+  </Settings>
+</ProjectConfiguration>

+ 5 - 0
.ncrunch/MobileSandbox.v3.ncrunchproject

@@ -0,0 +1,5 @@
+<ProjectConfiguration>
+  <Settings>
+    <IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
+  </Settings>
+</ProjectConfiguration>

+ 27 - 27
Avalonia.sln

@@ -198,9 +198,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTestApp", "sampl
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.IntegrationTests.Appium", "tests\Avalonia.IntegrationTests.Appium\Avalonia.IntegrationTests.Appium.csproj", "{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}"
 EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web.Blazor", "src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj", "{25831348-EB2A-483E-9576-E8F6528674A5}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Browser", "Browser", "{86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsInteropTest", "samples\interop\WindowsInteropTest\WindowsInteropTest.csproj", "{26A98DA1-D89D-4A95-8152-349F404DA2E2}"
 EndProject
@@ -216,8 +214,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Te
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevGenerators", "src\tools\DevGenerators\DevGenerators.csproj", "{1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web", "src\Web\Avalonia.Web\Avalonia.Web.csproj", "{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox", "samples\MobileSandbox\MobileSandbox.csproj", "{3B8519C1-2F51-4F12-A348-120AB91D4532}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Android", "samples\MobileSandbox.Android\MobileSandbox.Android.csproj", "{C90FE60B-B01E-4F35-91D6-379D6966030F}"
@@ -226,9 +222,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.iOS", "sample
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Desktop", "samples\MobileSandbox.Desktop\MobileSandbox.Desktop.csproj", "{62D392C9-81CF-487F-92E8-598B2AF3FDCE}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Blazor.Web", "samples\ControlCatalog.Blazor.Web\ControlCatalog.Blazor.Web.csproj", "{6A710364-AE6D-40BD-968B-024311527AC2}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Browser", "src\Browser\Avalonia.Browser\Avalonia.Browser.csproj", "{4A39637C-9338-4925-A4DB-D072E292EC78}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Browser.Blazor", "src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj", "{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Web", "samples\ControlCatalog.Web\ControlCatalog.Web.csproj", "{8B3E8405-DE18-4048-A459-9CA4AC3319A2}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser.Blazor", "samples\ControlCatalog.Browser.Blazor\ControlCatalog.Browser.Blazor.csproj", "{90B08091-9BBD-4362-B712-E9F2CC62B218}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -480,10 +480,6 @@ Global
 		{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.Build.0 = Release|Any CPU
-		{25831348-EB2A-483E-9576-E8F6528674A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{25831348-EB2A-483E-9576-E8F6528674A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.Build.0 = Release|Any CPU
 		{26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -512,10 +508,6 @@ Global
 		{1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}.Release|Any CPU.Build.0 = Release|Any CPU
-		{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Release|Any CPU.Build.0 = Release|Any CPU
 		{3B8519C1-2F51-4F12-A348-120AB91D4532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{3B8519C1-2F51-4F12-A348-120AB91D4532}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{3B8519C1-2F51-4F12-A348-120AB91D4532}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -533,14 +525,22 @@ Global
 		{62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Release|Any CPU.Build.0 = Release|Any CPU
-		{6A710364-AE6D-40BD-968B-024311527AC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{6A710364-AE6D-40BD-968B-024311527AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{6A710364-AE6D-40BD-968B-024311527AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{6A710364-AE6D-40BD-968B-024311527AC2}.Release|Any CPU.Build.0 = Release|Any CPU
-		{8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4A39637C-9338-4925-A4DB-D072E292EC78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4A39637C-9338-4925-A4DB-D072E292EC78}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4A39637C-9338-4925-A4DB-D072E292EC78}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4A39637C-9338-4925-A4DB-D072E292EC78}.Release|Any CPU.Build.0 = Release|Any CPU
+		{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{15B93A4C-1B46-43F6-B534-7B25B6E99932}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{15B93A4C-1B46-43F6-B534-7B25B6E99932}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{15B93A4C-1B46-43F6-B534-7B25B6E99932}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{15B93A4C-1B46-43F6-B534-7B25B6E99932}.Release|Any CPU.Build.0 = Release|Any CPU
+		{90B08091-9BBD-4362-B712-E9F2CC62B218}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{90B08091-9BBD-4362-B712-E9F2CC62B218}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{90B08091-9BBD-4362-B712-E9F2CC62B218}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{90B08091-9BBD-4362-B712-E9F2CC62B218}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -591,20 +591,20 @@ Global
 		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{676D6BFD-029D-4E43-BFC7-3892265CE251} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
-		{25831348-EB2A-483E-9576-E8F6528674A5} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
 		{26A98DA1-D89D-4A95-8152-349F404DA2E2} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
 		{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
 		{EABE2161-989B-42BF-BD8D-1E34B20C21F1} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
-		{76D39FF6-6B4F-46C4-93CD-E6FC4665739E} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
 		{3B8519C1-2F51-4F12-A348-120AB91D4532} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{C90FE60B-B01E-4F35-91D6-379D6966030F} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{FED9A71D-00D7-4F40-A9E4-1229EEA28EEB} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{62D392C9-81CF-487F-92E8-598B2AF3FDCE} = {9B9E3891-2366-4253-A952-D08BCEB71098}
-		{6A710364-AE6D-40BD-968B-024311527AC2} = {9B9E3891-2366-4253-A952-D08BCEB71098}
-		{8B3E8405-DE18-4048-A459-9CA4AC3319A2} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{4A39637C-9338-4925-A4DB-D072E292EC78} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
+		{47F8530C-F19B-4B1A-B4D6-EB231522AE5D} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
+		{15B93A4C-1B46-43F6-B534-7B25B6E99932} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{90B08091-9BBD-4362-B712-E9F2CC62B218} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

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

@@ -171,9 +171,7 @@
     _closed = false;
     _isEnabled = true;
 
-    [self backingScaleFactor];
     [self setOpaque:NO];
-    [self setBackgroundColor: [NSColor clearColor]];
 
     _isExtended = false;
     _isTransitioningToFullScreen = false;

+ 1 - 1
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@@ -90,7 +90,7 @@ BEGIN_INTERFACE_MAP()
 
     virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override;
 
-    virtual HRESULT SetBlurEnabled(bool enable) override;
+    virtual HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode) override;
 
     virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
             IAvnClipboard *clipboard, IAvnDndResultCallback *cb,

+ 3 - 2
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@@ -489,10 +489,11 @@ HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut)
     return S_OK;
 }
 
-HRESULT WindowBaseImpl::SetBlurEnabled(bool enable) {
+HRESULT WindowBaseImpl::SetTransparencyMode(AvnWindowTransparencyMode mode) {
     START_COM_CALL;
 
-    [StandardContainer ShowBlur:enable];
+    [Window setBackgroundColor: (mode != Transparent ? [NSColor windowBackgroundColor] : [NSColor clearColor])];
+    [StandardContainer ShowBlur: mode == Blur];
 
     return S_OK;
 }

+ 2 - 2
packages/Avalonia/AvaloniaBuildTasks.targets

@@ -74,7 +74,7 @@
       ReportImportance="$(AvaloniaXamlReportImportance)"/>
     <Exec 
       Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
-      Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false"/>
+      Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:RuntimeIdentifier=$(RuntimeIdentifier) /p:BuildProjectReferences=false"/>
 
   </Target>
 
@@ -112,7 +112,7 @@
     />
     <Exec
       Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
-      Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileAvaloniaXaml /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false"/>
+      Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileAvaloniaXaml /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:RuntimeIdentifier=$(RuntimeIdentifier) /p:BuildProjectReferences=false"/>
   </Target>
 
   

+ 10 - 4
samples/BindingDemo/App.xaml

@@ -2,8 +2,14 @@
     xmlns="https://github.com/avaloniaui" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     x:Class="BindingDemo.App">
-    <Application.Styles>
-        <FluentTheme />
-        <StyleInclude Source="avares://Avalonia.Themes.Simple/Accents/BaseLight.xaml"/>
-    </Application.Styles>
+  <Application.Resources>
+    <ResourceDictionary>
+      <ResourceDictionary.MergedDictionaries>
+        <ResourceInclude Source="avares://Avalonia.Themes.Simple/Accents/BaseLight.xaml"/>
+      </ResourceDictionary.MergedDictionaries>
+    </ResourceDictionary>
+  </Application.Resources>
+  <Application.Styles>
+    <FluentTheme />
+   </Application.Styles>
 </Application>

+ 0 - 0
samples/ControlCatalog.Blazor.Web/App.razor → samples/ControlCatalog.Browser.Blazor/App.razor


+ 2 - 2
samples/ControlCatalog.Blazor.Web/App.razor.cs → samples/ControlCatalog.Browser.Blazor/App.razor.cs

@@ -1,7 +1,7 @@
 using Avalonia;
-using Avalonia.Web.Blazor;
+using Avalonia.Browser.Blazor;
 
-namespace ControlCatalog.Blazor.Web;
+namespace ControlCatalog.Browser.Blazor;
 
 public partial class App
 {

+ 3 - 3
samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj → samples/ControlCatalog.Browser.Blazor/ControlCatalog.Browser.Blazor.csproj

@@ -15,15 +15,15 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
-    <ProjectReference Include="..\..\src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj" />
+    <ProjectReference Include="..\..\src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj" />
     <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
   </ItemGroup>
 
   <Import Project="..\..\build\ReferenceCoreLibraries.props" />
   <Import Project="..\..\build\BuildTargets.targets" />
 
-  <Import Project="..\..\src\Web\Avalonia.Web\Avalonia.Web.props" />
-  <Import Project="..\..\src\Web\Avalonia.Web\Avalonia.Web.targets" />
+  <Import Project="..\..\src\Browser\Avalonia.Browser\Avalonia.Browser.props" />
+  <Import Project="..\..\src\Browser\Avalonia.Browser\Avalonia.Browser.targets" />
 
 </Project>
 

+ 1 - 1
samples/ControlCatalog.Blazor.Web/Pages/Index.razor → samples/ControlCatalog.Browser.Blazor/Pages/Index.razor

@@ -1,5 +1,5 @@
 @page "/"
 
-@using Avalonia.Web.Blazor
+@using Avalonia.Browser.Blazor
 
 <AvaloniaView />

+ 1 - 1
samples/ControlCatalog.Blazor.Web/Program.cs → samples/ControlCatalog.Browser.Blazor/Program.cs

@@ -3,7 +3,7 @@ using System.Net.Http;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
 using Microsoft.Extensions.DependencyInjection;
-using ControlCatalog.Blazor.Web;
+using ControlCatalog.Browser.Blazor;
 
 public class Program
 {

+ 0 - 0
samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json → samples/ControlCatalog.Browser.Blazor/Properties/launchSettings.json


+ 0 - 0
samples/ControlCatalog.Blazor.Web/Shared/MainLayout.razor → samples/ControlCatalog.Browser.Blazor/Shared/MainLayout.razor


+ 1 - 1
samples/ControlCatalog.Blazor.Web/_Imports.razor → samples/ControlCatalog.Browser.Blazor/_Imports.razor

@@ -6,5 +6,5 @@
 @using Microsoft.AspNetCore.Components.Web.Virtualization
 @using Microsoft.AspNetCore.Components.WebAssembly.Http
 @using Microsoft.JSInterop
-@using ControlCatalog.Blazor.Web.Shared
+@using ControlCatalog.Browser.Blazor.Shared
 @using SkiaSharp

+ 0 - 0
samples/ControlCatalog.Blazor.Web/wwwroot/css/app.css → samples/ControlCatalog.Browser.Blazor/wwwroot/css/app.css


+ 0 - 0
samples/ControlCatalog.Blazor.Web/wwwroot/favicon.ico → samples/ControlCatalog.Browser.Blazor/wwwroot/favicon.ico


+ 0 - 0
samples/ControlCatalog.Blazor.Web/wwwroot/index.html → samples/ControlCatalog.Browser.Blazor/wwwroot/index.html


+ 3 - 3
samples/ControlCatalog.Web/ControlCatalog.Web.csproj → samples/ControlCatalog.Browser/ControlCatalog.Browser.csproj

@@ -26,7 +26,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
-    <ProjectReference Include="..\..\src\Web\Avalonia.Web\Avalonia.Web.csproj" />
+    <ProjectReference Include="..\..\src\Browser\Avalonia.Browser\Avalonia.Browser.csproj" />
     <ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
   </ItemGroup>
 
@@ -39,6 +39,6 @@
     <WasmExtraFilesToDeploy Include="app.css" />
   </ItemGroup>
 
-  <Import Project="..\..\src\Web\Avalonia.Web\Avalonia.Web.props" />
-  <Import Project="..\..\src\Web\Avalonia.Web\Avalonia.Web.targets" />
+  <Import Project="..\..\src\Browser\Avalonia.Browser\Avalonia.Browser.props" />
+  <Import Project="..\..\src\Browser\Avalonia.Browser\Avalonia.Browser.targets" />
 </Project>

+ 2 - 2
samples/ControlCatalog.Web/EmbedSample.Browser.cs → samples/ControlCatalog.Browser/EmbedSample.Browser.cs

@@ -1,11 +1,11 @@
 using System;
 using System.Runtime.InteropServices.JavaScript;
 using Avalonia.Platform;
-using Avalonia.Web;
+using Avalonia.Browser;
 
 using ControlCatalog.Pages;
 
-namespace ControlCatalog.Web;
+namespace ControlCatalog.Browser;
 
 public class EmbedSampleWeb : INativeDemoControl
 {

+ 0 - 0
samples/ControlCatalog.Web/Logo.svg → samples/ControlCatalog.Browser/Logo.svg


+ 2 - 2
samples/ControlCatalog.Web/Program.cs → samples/ControlCatalog.Browser/Program.cs

@@ -1,8 +1,8 @@
 using System.Runtime.Versioning;
 using Avalonia;
-using Avalonia.Web;
+using Avalonia.Browser;
 using ControlCatalog;
-using ControlCatalog.Web;
+using ControlCatalog.Browser;
 
 [assembly:SupportedOSPlatform("browser")]
 

+ 0 - 0
samples/ControlCatalog.Web/Roots.xml → samples/ControlCatalog.Browser/Roots.xml


+ 0 - 0
samples/ControlCatalog.Web/app.css → samples/ControlCatalog.Browser/app.css


+ 0 - 0
samples/ControlCatalog.Web/embed.js → samples/ControlCatalog.Browser/embed.js


+ 0 - 0
samples/ControlCatalog.Web/favicon.ico → samples/ControlCatalog.Browser/favicon.ico


+ 0 - 0
samples/ControlCatalog.Web/index.html → samples/ControlCatalog.Browser/index.html


+ 0 - 0
samples/ControlCatalog.Web/main.js → samples/ControlCatalog.Browser/main.js


+ 0 - 0
samples/ControlCatalog.Web/runtimeconfig.template.json → samples/ControlCatalog.Browser/runtimeconfig.template.json


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

@@ -99,6 +99,15 @@ namespace ControlCatalog.NetCore
                 SilenceConsole();
                 return builder.StartLinuxDrm(args, scaling: GetScaling());
             }
+            else if (args.Contains("--dxgi"))
+            {
+                builder.With(new Win32PlatformOptions()
+                {
+                    UseLowLatencyDxgiSwapChain = true,
+                    UseWindowsUIComposition = false
+                });
+                return builder.StartWithClassicDesktopLifetime(args);
+            }
             else
                 return builder.StartWithClassicDesktopLifetime(args);
         }

+ 11 - 0
samples/ControlCatalog.NetCore/Properties/launchSettings.json

@@ -0,0 +1,11 @@
+{
+  "profiles": {
+    "ControlCatalog.NetCore": {
+      "commandName": "Project"
+    },
+    "Dxgi": {
+      "commandName": "Project",
+      "commandLineArgs": "--dxgi"
+    }
+  }
+}

+ 10 - 0
samples/ControlCatalog/ControlCatalog.csproj

@@ -33,4 +33,14 @@
   </ItemGroup>
 
   <Import Project="..\..\build\BuildTargets.targets" />
+
+  <ItemGroup>
+    <None Remove="Pages\CustomDrawing.xaml" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <AvaloniaResource Update="Pages\CustomDrawing.xaml">
+      <Generator></Generator>
+    </AvaloniaResource>
+  </ItemGroup>
 </Project>

+ 29 - 0
samples/ControlCatalog/Converter/DegToRadConverter.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using Avalonia.Data.Converters;
+
+namespace ControlCatalog.Converter
+{
+    public class DegToRadConverter : IValueConverter
+    {
+        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+        {
+            if (value is double rad)
+            {
+                return rad * 180.0d / Math.PI;
+            }
+            return 0.0d;
+        }
+
+        public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+        {
+            if (value is double deg)
+            {
+                return deg / 180.0d * Math.PI;
+            }
+            return 0.0d;
+        }
+    }
+}

+ 3 - 0
samples/ControlCatalog/MainView.xaml

@@ -66,6 +66,9 @@
       <TabItem Header="Cursor" ScrollViewer.VerticalScrollBarVisibility="Disabled">
         <pages:CursorPage />
       </TabItem>
+      <TabItem Header="Custom Drawing" ScrollViewer.VerticalScrollBarVisibility="Disabled">
+        <pages:CustomDrawing/>
+      </TabItem>
       <TabItem Header="DataGrid"
                ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                ScrollViewer.VerticalScrollBarVisibility="Disabled">

+ 1 - 1
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs

@@ -143,7 +143,7 @@ namespace ControlCatalog.Pages
                     .OfType<AutoCompleteBox>();
         }
 
-        private bool StringContains(string str, string? query)
+        private static bool StringContains(string str, string? query)
         {
             if (query == null) return false;
             return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;

+ 107 - 0
samples/ControlCatalog/Pages/CustomDrawing.xaml

@@ -0,0 +1,107 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:local="using:ControlCatalog.Pages"
+             xmlns:converters="using:ControlCatalog.Converter"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             x:Class="ControlCatalog.Pages.CustomDrawing">
+
+  <UserControl.Resources>
+    <converters:DegToRadConverter x:Key="DegToRadConverter"/>
+  </UserControl.Resources>
+  
+  <Grid>
+    <Grid.ColumnDefinitions>
+      <ColumnDefinition/>
+      <ColumnDefinition/>
+      <ColumnDefinition/>
+    </Grid.ColumnDefinitions>
+    <Grid.RowDefinitions>
+      <RowDefinition Height="Auto"/>
+      <RowDefinition/>
+    </Grid.RowDefinitions>
+
+    <StackPanel Orientation="Vertical">
+      <TextBlock Text="Translation" HorizontalAlignment="Center"/>
+      <Grid ColumnDefinitions="*,*"
+            RowDefinitions="Auto,Auto"
+            >
+        <TextBlock Text="Horizontal"/>
+        <TextBlock Text="Vertical" Grid.Column="1"/>
+        <TextBox IsEnabled="False" 
+                 Text="{Binding ElementName=CustomDrawingControl,
+                  Path=ViewportCenterX,
+                  Mode=OneWay,
+                  StringFormat=\{0:g4\}}"
+                 Grid.Row="1"
+                 />
+        <TextBox IsEnabled="False"
+                 Text="{Binding ElementName=CustomDrawingControl,
+                  Path=ViewportCenterY, 
+                  Mode=OneWay,
+                  StringFormat=\{0:g4\}}"
+                 Grid.Row="1" Grid.Column="1"
+                 />
+      </Grid>
+    </StackPanel>
+
+    <StackPanel Orientation="Vertical" Grid.Column="1"
+                >
+      <TextBlock Text="Rotation" HorizontalAlignment="Center"/>
+      <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
+                  >
+        <Button Content="➖" Width="40" Height="40"
+                VerticalContentAlignment="Center"
+                VerticalAlignment="Center"
+                Click="RotateMinus"
+                />
+        <TextBox IsEnabled="False"
+                 Text="{Binding ElementName=CustomDrawingControl,
+                  Path=Rotation, 
+                  Converter={StaticResource DegToRadConverter},
+                  Mode=OneWay,
+                  StringFormat=\{0:g4\}}"
+                 Grid.Row="1" Grid.Column="1"
+                 />
+        <Button Content="➕" Width="40" Height="40"
+                VerticalContentAlignment="Center"
+                VerticalAlignment="Center"
+                Click="RotatePlus"
+                />
+      </StackPanel>
+    </StackPanel>
+
+    <StackPanel Orientation="Vertical" Grid.Column="2"
+                >
+      <TextBlock Text="Scale" HorizontalAlignment="Center"/>
+      <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
+                  >
+        <Button Content="➖" Width="40" Height="40"
+                VerticalContentAlignment="Center"
+                VerticalAlignment="Center"
+                Click="ZoomOut"
+                />
+        <TextBox IsEnabled="False"
+                 Text="{Binding ElementName=CustomDrawingControl,
+                  Path=Scale, 
+                  Mode=OneWay,
+                  StringFormat=\{0:g4\}}"
+                 Grid.Row="1" Grid.Column="1"
+                 />
+        <Button Content="➕" Width="40" Height="40"
+                VerticalContentAlignment="Center"
+                VerticalAlignment="Center"
+                Click="ZoomIn"
+                />
+      </StackPanel>
+    </StackPanel>
+    
+    <Grid Grid.Row="1" Grid.ColumnSpan="3" ClipToBounds="True">
+      <local:CustomDrawingExampleControl
+        x:Name="CustomDrawingControl"
+        />
+    </Grid>
+  </Grid>
+  
+</UserControl>

+ 67 - 0
samples/ControlCatalog/Pages/CustomDrawing.xaml.cs

@@ -0,0 +1,67 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages
+{
+    public partial class CustomDrawing : UserControl
+    {
+        public CustomDrawing()
+        {
+            InitializeComponent();
+        }
+
+        private CustomDrawingExampleControl? _customControl;
+        public CustomDrawingExampleControl CustomDrawingControl
+        {
+            get
+            {
+                if (_customControl is not null)
+                    return _customControl;
+                throw new System.Exception("Control did not get initialized");
+            }
+        }
+
+        private void InitializeComponent()
+        {
+            AvaloniaXamlLoader.Load(this);
+            var cntrl = this.FindControl<CustomDrawingExampleControl>("CustomDrawingControl");
+            if (cntrl != null)
+            {
+                _customControl = cntrl;
+            }
+            else
+            {
+                // be sad about it 
+            }
+        }
+
+        private void RotateMinus (object? sender, RoutedEventArgs e)
+        {
+            if (_customControl is null) return;
+            _customControl.Rotation -= Math.PI / 20.0d;
+        }
+
+        private void RotatePlus(object? sender, RoutedEventArgs e)
+        {
+            if (_customControl is null)
+                return;
+            _customControl.Rotation += Math.PI / 20.0d;
+        }
+
+        private void ZoomIn(object? sender, RoutedEventArgs e)
+        {
+            if (_customControl is null)
+                return;
+            _customControl.Scale *= 1.2d;
+        }
+
+        private void ZoomOut(object? sender, RoutedEventArgs e)
+        {
+            if (_customControl is null)
+                return;
+            _customControl.Scale /= 1.2d;
+        }
+    }
+}

+ 215 - 0
samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs

@@ -0,0 +1,215 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Input;
+using Avalonia.Threading;
+using Avalonia.Controls.Shapes;
+
+namespace ControlCatalog.Pages
+{
+    public class CustomDrawingExampleControl : Control
+    {
+        private Point _cursorPoint;
+
+
+        public StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Scale), 1.0d);
+        public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); }
+
+        public StyledProperty<double> RotationProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Rotation));
+        /// <summary>
+        /// Rotation, measured in Radians!
+        /// </summary>
+        public double Rotation
+        {
+            get => GetValue(RotationProperty);
+            set
+            {
+                double valueToUse = value % (Math.PI * 2);
+                SetValue(RotationProperty, valueToUse);
+            }
+        }
+
+        public StyledProperty<double> ViewportCenterYProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterY), 0.0d);
+        public double ViewportCenterY { get => GetValue(ViewportCenterYProperty); set => SetValue(ViewportCenterYProperty, value); }
+
+        public StyledProperty<double> ViewportCenterXProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterX), 0.0d);
+        public double ViewportCenterX { get => GetValue(ViewportCenterXProperty); set => SetValue(ViewportCenterXProperty, value); }
+
+        private IPen _pen;
+
+        private System.Diagnostics.Stopwatch _timeKeeper = System.Diagnostics.Stopwatch.StartNew();
+
+        private bool _isPointerCaptured = false;
+
+        public CustomDrawingExampleControl()
+        {
+            _pen = new Pen(new SolidColorBrush(Colors.Black), lineCap: PenLineCap.Round);
+
+            var _arc = new ArcSegment()
+            {
+                IsLargeArc = false,
+                Point = new Point(0, 0),
+                RotationAngle = 0,
+                Size = new Size(25, 25),
+                SweepDirection = SweepDirection.Clockwise,
+
+            };
+            StreamGeometry sg = new StreamGeometry();
+            var cntx = sg.Open();
+            cntx.BeginFigure(new Point(-25.0d, -10.0d), false);
+            cntx.ArcTo(new Point(25.0d, -10.0d), new Size(10.0d, 10.0d), 0.0d, false, SweepDirection.Clockwise);
+            cntx.EndFigure(true);
+            _smileGeometry = sg.Clone();
+        }
+
+        private Geometry _smileGeometry;
+
+        protected override void OnPointerMoved(PointerEventArgs e)
+        {
+            base.OnPointerMoved(e);
+
+            Point previousPoint = _cursorPoint;
+
+            _cursorPoint = e.GetPosition(this);
+
+            if (_isPointerCaptured)
+            {
+                Point oldWorldPoint = UIPointToWorldPoint(previousPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
+                Point newWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
+
+                Vector diff = newWorldPoint - oldWorldPoint;
+
+                ViewportCenterX -= diff.X;
+                ViewportCenterY -= diff.Y;
+            }
+        }
+
+        protected override void OnPointerPressed(PointerPressedEventArgs e)
+        {
+            e.Handled = true;
+            e.Pointer.Capture(this);
+            _isPointerCaptured = true;
+            base.OnPointerPressed(e);
+        }
+
+        protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
+        {
+            base.OnPointerWheelChanged(e);
+            var oldScale = Scale;
+            Scale *= (1.0d + e.Delta.Y / 12.0d);
+
+            Point oldWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, oldScale, Rotation);
+            Point newWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
+
+            Vector diff = newWorldPoint - oldWorldPoint;
+
+            ViewportCenterX -= diff.X;
+            ViewportCenterY -= diff.Y;
+        }
+
+        protected override void OnPointerReleased(PointerReleasedEventArgs e)
+        {
+            e.Pointer.Capture(null);
+            _isPointerCaptured = false;
+            base.OnPointerReleased(e);
+        }
+
+        public override void Render(DrawingContext context)
+        {
+            var localBounds = new Rect(new Size(this.Bounds.Width, this.Bounds.Height));
+            var clip = context.PushClip(this.Bounds);
+            context.DrawRectangle(Brushes.White, _pen, localBounds, 1.0d);
+
+            var halfMax = Math.Max(this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d) * Math.Sqrt(2.0d);
+            var halfMin = Math.Min(this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d) / 1.3d;
+            var halfWidth = this.Bounds.Width / 2.0d;
+            var halfHeight = this.Bounds.Height / 2.0d;
+
+            // 0,0 refers to the top-left of the control now. It is not prime time to draw gui stuff because it'll be under the world 
+
+            var translateModifier = context.PushPreTransform(Avalonia.Matrix.CreateTranslation(new Avalonia.Vector(halfWidth, halfHeight)));
+
+            // now 0,0 refers to the ViewportCenter(X,Y). 
+            var rotationMatrix = Avalonia.Matrix.CreateRotation(Rotation);
+            var rotationModifier = context.PushPreTransform(rotationMatrix);
+
+            // everything is rotated but not scaled 
+
+            var scaleModifier = context.PushPreTransform(Avalonia.Matrix.CreateScale(Scale, -Scale));
+
+            var mapPositionModifier = context.PushPreTransform(Matrix.CreateTranslation(new Vector(-ViewportCenterX, -ViewportCenterY)));
+
+            // now everything is rotated and scaled, and at the right position, now we're drawing strictly in world coordinates
+
+            context.DrawEllipse(Brushes.White, _pen, new Point(0.0d, 0.0d), 50.0d, 50.0d);
+            context.DrawLine(_pen, new Point(-25.0d, -5.0d), new Point(-25.0d, 15.0d));
+            context.DrawLine(_pen, new Point(25.0d, -5.0d), new Point(25.0d, 15.0d));
+            context.DrawGeometry(null, _pen, _smileGeometry);
+
+            Point cursorInWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
+            context.DrawEllipse(Brushes.Gray, _pen, cursorInWorldPoint, 20.0d, 20.0d);
+            
+
+            for (int i = 0; i < 10; i++)
+            {
+                double orbitRadius = i * 100 + 200;
+                var orbitInput = ((_timeKeeper.Elapsed.TotalMilliseconds + 987654d) / orbitRadius) / 10.0d;
+                if (i % 3 == 0)
+                    orbitInput *= -1;
+                Point orbitPosition = new Point(Math.Sin(orbitInput) * orbitRadius, Math.Cos(orbitInput) * orbitRadius);
+                context.DrawEllipse(Brushes.Gray, _pen, orbitPosition, 20.0d, 20.0d);
+            }
+
+
+            // end drawing the world 
+
+            mapPositionModifier.Dispose();
+
+            scaleModifier.Dispose();
+
+            rotationModifier.Dispose();
+            translateModifier.Dispose();
+
+            // this is prime time to draw gui stuff 
+
+            context.DrawLine(_pen, _cursorPoint + new Vector(-20, 0), _cursorPoint + new Vector(20, 0));
+            context.DrawLine(_pen, _cursorPoint + new Vector(0, -20), _cursorPoint + new Vector(0, 20));
+
+            clip.Dispose();
+
+            // oh and draw again when you can, no rush, right? 
+            Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);
+        }
+
+        private Point UIPointToWorldPoint(Point inPoint, double viewportCenterX, double viewportCenterY, double scale, double rotation)
+        {
+            Point workingPoint = new Point(inPoint.X, -inPoint.Y);
+            workingPoint += new Vector(-this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d);
+            workingPoint /= scale;
+
+            workingPoint = Matrix.CreateRotation(rotation).Transform(workingPoint);
+
+            workingPoint += new Vector(viewportCenterX, viewportCenterY);
+
+            return workingPoint;
+        }
+
+        private Point WorldPointToUIPoint(Point inPoint, double viewportCenterX, double viewportCenterY, double scale, double rotation)
+        {
+            Point workingPoint = new Point(inPoint.X, inPoint.Y);
+
+            workingPoint -= new Vector(viewportCenterX, viewportCenterY);
+            // undo rotation
+            workingPoint = Matrix.CreateRotation(-rotation).Transform(workingPoint);
+            workingPoint *= scale;
+            workingPoint -= new Vector(-this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d);
+            workingPoint = new Point(workingPoint.X, -workingPoint.Y);
+
+            return workingPoint;
+        }
+    }
+}

+ 33 - 18
samples/ControlCatalog/Pages/DataGridPage.xaml

@@ -14,22 +14,6 @@
       <Setter Property="Background" Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}" />
     </ControlTheme>
   </UserControl.Resources>
-  <UserControl.Styles>
-    <Style Selector="DataGridColumnHeader:nth-last-child(1)">
-      <Setter Property="FontWeight" Value="Bold" />
-    </Style>
-    <Style Selector="DataGridCell:nth-last-child(1)">
-      <Setter Property="FontWeight" Value="Bold" />
-    </Style>
-    <Style Selector="DataGrid#dataGridGrouping DataGridRow:nth-child(5n+3)">
-      <Setter Property="Foreground" Value="Red" />
-      <Setter Property="FontWeight" Value="Bold" />
-    </Style>
-    <Style Selector="DataGrid#dataGridGrouping DataGridRow:nth-last-child(5n+1)">
-      <Setter Property="Foreground" Value="Blue" />
-      <Setter Property="FontWeight" Value="Bold" />
-    </Style>
-  </UserControl.Styles>
   <Grid RowDefinitions="Auto,Auto,*">
     <StackPanel Orientation="Vertical" Spacing="4" Grid.Row="0">
       <TextBlock Classes="h2">A control for displaying and interacting with a data source.</TextBlock>
@@ -45,8 +29,7 @@
           <CheckBox x:Name="ShowGDP"  IsChecked="True"  Content="Toggle GDP Column Visibility"
                     DockPanel.Dock="Top"/>
           <DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All"
-                    RowBackground="#1000"
-                    AlternatingRowBackground="#1fff">
+                    RowBackground="#1000">
             <DataGrid.Columns>
               <!-- Using HeaderTemplate -->
               <DataGridTextColumn Header="Country" HeaderTemplate="{StaticResource Demo.DataTemplates.CountryHeader}" Binding="{Binding Name}"  Width="6*" x:DataType="local:Country" />
@@ -59,6 +42,24 @@
                                   IsVisible="{Binding #ShowGDP.IsChecked}"
                                   x:DataType="local:Country" />
             </DataGrid.Columns>
+            <DataGrid.CellTheme>
+              <ControlTheme TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
+                <ControlTheme.Children>
+                  <Style Selector="^:nth-child(1)">
+                    <Setter Property="FontWeight" Value="Bold" />
+                  </Style>
+                </ControlTheme.Children>
+              </ControlTheme>
+            </DataGrid.CellTheme>
+            <DataGrid.ColumnHeaderTheme>
+              <ControlTheme TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
+                <ControlTheme.Children>
+                  <Style Selector="^:nth-child(1)">
+                    <Setter Property="FontWeight" Value="Bold" />
+                  </Style>
+                </ControlTheme.Children>
+              </ControlTheme>
+            </DataGrid.ColumnHeaderTheme>
           </DataGrid>
         </DockPanel>
       </TabItem>
@@ -71,6 +72,20 @@
             <DataGridTextColumn DisplayIndex="2" Header="Area" Binding="{Binding Area}" Width="3*" x:DataType="local:Country" />
             <DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*" x:DataType="local:Country" />
           </DataGrid.Columns>
+          <DataGrid.RowTheme>
+            <ControlTheme TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
+              <ControlTheme.Children>
+                <Style Selector="^:nth-child(5n+3)">
+                  <Setter Property="Foreground" Value="Red" />
+                  <Setter Property="FontWeight" Value="Bold" />
+                </Style>
+                <Style Selector="^:nth-last-child(5n+1)">
+                  <Setter Property="Foreground" Value="Blue" />
+                  <Setter Property="FontWeight" Value="Bold" />
+                </Style>
+              </ControlTheme.Children>
+            </ControlTheme>
+          </DataGrid.RowTheme>
         </DataGrid>
       </TabItem>
       <TabItem x:Name="EditableTab" Header="Editable">

+ 18 - 0
samples/ControlCatalog/Pages/ExpanderPage.xaml

@@ -52,6 +52,24 @@
         </StackPanel>
       </Expander>
       <CheckBox IsChecked="{Binding Rounded}">Rounded</CheckBox>
+      <Expander x:Name="CollapsingDisabledExpander"
+                Header="Collapsing Disabled"
+                IsExpanded="True"
+                ExpandDirection="Down"
+                CornerRadius="{Binding CornerRadius}">
+        <StackPanel>
+          <TextBlock>Expanded content</TextBlock>
+        </StackPanel>
+      </Expander>
+      <Expander x:Name="ExpandingDisabledExpander"
+                Header="Expanding Disabled"
+                IsExpanded="False"
+                ExpandDirection="Down"
+                CornerRadius="{Binding CornerRadius}">
+        <StackPanel>
+          <TextBlock>Expanded content</TextBlock>
+        </StackPanel>
+      </Expander>
     </StackPanel>
   </StackPanel>
 </UserControl>

+ 6 - 0
samples/ControlCatalog/Pages/ExpanderPage.xaml.cs

@@ -10,6 +10,12 @@ namespace ControlCatalog.Pages
         {
             this.InitializeComponent();
             DataContext = new ExpanderPageViewModel();
+
+            var CollapsingDisabledExpander = this.Get<Expander>("CollapsingDisabledExpander");
+            var ExpandingDisabledExpander = this.Get<Expander>("ExpandingDisabledExpander");
+
+            CollapsingDisabledExpander.Collapsing += (s, e) => { e.Handled = true; };
+            ExpandingDisabledExpander.Expanding += (s, e) => { e.Handled = true; };
         }
 
         private void InitializeComponent()

+ 1 - 0
samples/ControlCatalog/Pages/ListBoxPage.xaml

@@ -32,6 +32,7 @@
     </StackPanel>
     <ListBox Items="{Binding Items}"
              Selection="{Binding Selection}"
+             DisplayMemberBinding="{Binding (viewModels:ItemModel).ID, StringFormat='{}Item {0:N0}'}"
              AutoScrollToSelectedItem="{Binding AutoScrollToSelectedItem}"
              SelectionMode="{Binding SelectionMode^}"
              WrapSelection="{Binding WrapSelection}"/>

+ 1 - 7
samples/ControlCatalog/Pages/TabControlPage.xaml

@@ -53,14 +53,8 @@
                 <TabControl
                     Items="{Binding Tabs}"
                     Margin="0 16"
+                    HeaderDisplayMemberBinding="{Binding Header, x:DataType=viewModels:TabControlPageViewModelItem}"
                     TabStripPlacement="{Binding TabPlacement}">
-                    <TabControl.ItemTemplate>
-                        <DataTemplate x:DataType="viewModels:TabControlPageViewModelItem">
-                            <TextBlock
-                                Text="{Binding Header}">
-                            </TextBlock>
-                        </DataTemplate>
-                    </TabControl.ItemTemplate>
                     <TabControl.ContentTemplate>
                         <DataTemplate x:DataType="viewModels:TabControlPageViewModelItem">
                             <StackPanel Orientation="Vertical" Spacing="8">

+ 3 - 3
samples/ControlCatalog/Pages/TabControlPage.xaml.cs

@@ -25,13 +25,13 @@ namespace ControlCatalog.Pages
                     {
                         Header = "Arch",
                         Text = "This is the first templated tab page.",
-                        Image = LoadBitmap("avares://ControlCatalog/Assets/delicate-arch-896885_640.jpg"),
+                        Image = TabControlPage.LoadBitmap("avares://ControlCatalog/Assets/delicate-arch-896885_640.jpg"),
                     },
                     new TabControlPageViewModelItem
                     {
                         Header = "Leaf",
                         Text = "This is the second templated tab page.",
-                        Image = LoadBitmap("avares://ControlCatalog/Assets/maple-leaf-888807_640.jpg"),
+                        Image = TabControlPage.LoadBitmap("avares://ControlCatalog/Assets/maple-leaf-888807_640.jpg"),
                     },
                     new TabControlPageViewModelItem
                     {
@@ -49,7 +49,7 @@ namespace ControlCatalog.Pages
             AvaloniaXamlLoader.Load(this);
         }
 
-        private IBitmap LoadBitmap(string uri)
+        private static IBitmap LoadBitmap(string uri)
         {
             var assets = AvaloniaLocator.Current!.GetService<IAssetLoader>()!;
             return new Bitmap(assets.Open(new Uri(uri)));

+ 31 - 5
samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using System.Reactive;
 using Avalonia.Controls;
 using Avalonia.Controls.Selection;
+using ControlCatalog.Pages;
 using MiniMvvm;
 
 namespace ControlCatalog.ViewModels
@@ -20,9 +21,9 @@ namespace ControlCatalog.ViewModels
 
         public ListBoxPageViewModel()
         {
-            Items = new ObservableCollection<string>(Enumerable.Range(1, 10000).Select(i => GenerateItem()));
+            Items = new ObservableCollection<ItemModel>(Enumerable.Range(1, 10000).Select(i => GenerateItem()));
             
-            Selection = new SelectionModel<string>();
+            Selection = new SelectionModel<ItemModel>();
             Selection.Select(1);
 
             _selectionMode = this.WhenAnyValue(
@@ -58,8 +59,8 @@ namespace ControlCatalog.ViewModels
             });
         }
 
-        public ObservableCollection<string> Items { get; }
-        public SelectionModel<string> Selection { get; }
+        public ObservableCollection<ItemModel> Items { get; }
+        public SelectionModel<ItemModel> Selection { get; }
         public IObservable<SelectionMode> SelectionMode => _selectionMode;
 
         public bool Multiple
@@ -96,6 +97,31 @@ namespace ControlCatalog.ViewModels
         public MiniCommand RemoveItemCommand { get; }
         public MiniCommand SelectRandomItemCommand { get; }
 
-        private string GenerateItem() => $"Item {_counter++.ToString()}";
+        private ItemModel GenerateItem() => new ItemModel(_counter ++);  
+    }
+
+    /// <summary>
+    /// An Item model for the <see cref="ListBoxPage"/>
+    /// </summary>
+    public class ItemModel
+    {
+        /// <summary>
+        /// Creates a new ItemModel with the given ID
+        /// </summary>
+        /// <param name="id">The ID to display</param>
+        public ItemModel(int id)
+        {
+            ID = id;
+        }
+
+        /// <summary>
+        /// The ID of this Item
+        /// </summary>
+        public int ID { get; }
+
+        public override string ToString()
+        {
+            return $"Item {ID}";
+        }
     }
 }

+ 3 - 3
samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs

@@ -293,10 +293,10 @@ namespace ControlCatalog.ViewModels
         /// <remarks>
         /// Any one of the parameters may be null, but not both.
         /// </remarks>
-        private static IVisual GetVisualParent(IVisual? from, IVisual? to)
+        private static Visual GetVisualParent(Visual? from, Visual? to)
         {
-            var p1 = (from ?? to)!.VisualParent;
-            var p2 = (to ?? from)!.VisualParent;
+            var p1 = (from ?? to)!.GetVisualParent();
+            var p2 = (to ?? from)!.GetVisualParent();
 
             if (p1 != null && p2 != null && p1 != p2)
             {

+ 4 - 0
src/Android/Avalonia.Android/AndroidInputMethod.cs

@@ -95,6 +95,10 @@ namespace Avalonia.Android
 
                 _imm.UpdateSelection(_host, surroundingText.AnchorOffset, surroundingText.CursorOffset, surroundingText.AnchorOffset, surroundingText.CursorOffset);
             }
+            else
+            {
+                _imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.ImplicitOnly);
+            }
         }
 
         private void SurroundingTextChanged(object sender, EventArgs e)

+ 25 - 0
src/Android/Avalonia.Android/AvaloniaMainActivity.cs

@@ -4,9 +4,12 @@ using Android.Content;
 using Android.Content.Res;
 using Android.OS;
 using Android.Runtime;
+using Android.Views;
 using AndroidX.AppCompat.App;
 using AndroidX.Lifecycle;
 
+using AndroidRect = Android.Graphics.Rect;
+
 namespace Avalonia.Android
 {
     public abstract class AvaloniaMainActivity : AppCompatActivity, IActivityResultHandler
@@ -15,6 +18,7 @@ namespace Avalonia.Android
 
         public Action<int, Result, Intent> ActivityResult { get; set; }
         internal AvaloniaView View;
+        private GlobalLayoutListener _listener;
 
         protected override void OnCreate(Bundle savedInstanceState)
         {
@@ -32,6 +36,10 @@ namespace Avalonia.Android
             base.OnCreate(savedInstanceState);
 
             SetContentView(View);
+
+            _listener = new GlobalLayoutListener(View);
+
+            View.ViewTreeObserver?.AddOnGlobalLayoutListener(_listener);
         }
 
         public object Content
@@ -57,6 +65,8 @@ namespace Avalonia.Android
         {
             View.Content = null;
 
+            View.ViewTreeObserver?.RemoveOnGlobalLayoutListener(_listener);
+
             base.OnDestroy();
         }
 
@@ -66,5 +76,20 @@ namespace Avalonia.Android
 
             ActivityResult?.Invoke(requestCode, resultCode, data);
         }
+
+        class GlobalLayoutListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
+        {
+            private AvaloniaView _view;
+
+            public GlobalLayoutListener(AvaloniaView view)
+            {
+                _view = view;
+            }
+
+            public void OnGlobalLayout()
+            {
+                _view.TopLevelImpl?.Resize(_view.TopLevelImpl.ClientSize);
+            }
+        }
     }
 }

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

@@ -26,6 +26,7 @@ using Avalonia.Rendering;
 using Avalonia.Rendering.Composition;
 using Java.Lang;
 using Math = System.Math;
+using AndroidRect = Android.Graphics.Rect;
 
 namespace Avalonia.Android.Platform.SkiaPlatform
 {
@@ -63,7 +64,21 @@ namespace Avalonia.Android.Platform.SkiaPlatform
 
         public IInputRoot InputRoot { get; private set; }
 
-        public virtual Size ClientSize => Size.ToSize(RenderScaling);
+        public virtual Size ClientSize
+        {
+            get
+            {
+                AndroidRect rect = new AndroidRect();
+                AndroidRect intersection = new AndroidRect();
+
+                _view.GetWindowVisibleDisplayFrame(intersection);
+                _view.GetGlobalVisibleRect(rect);
+
+                intersection.Intersect(rect);
+
+                return new PixelSize(intersection.Right - intersection.Left, intersection.Bottom - intersection.Top).ToSize(RenderScaling);
+            }
+        }
 
         public Size? FrameSize => null;
 
@@ -94,7 +109,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
                 ? new CompositingRenderer(root, AndroidPlatform.Compositor)
                 : AndroidPlatform.Options.UseDeferredRendering
                     ? new DeferredRenderer(root, AvaloniaLocator.Current.GetRequiredService<IRenderLoop>()) { RenderOnlyOnRenderThread = true }
-                    : new ImmediateRenderer(root);
+                    : new ImmediateRenderer((Visual)root);
 
         public virtual void Hide()
         {
@@ -149,6 +164,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform
             Resized?.Invoke(size, PlatformResizeReason.Unspecified);
         }
 
+        internal void Resize(Size size)
+        {
+            Resized?.Invoke(size, PlatformResizeReason.Layout);
+        }
+
         class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo
         {
             private readonly TopLevelImpl _tl;

+ 1 - 1
src/Avalonia.Base/Animation/PageSlide.cs

@@ -157,7 +157,7 @@ namespace Avalonia.Animation
         /// <remarks>
         /// Any one of the parameters may be null, but not both.
         /// </remarks>
-        protected static IVisual GetVisualParent(IVisual? from, IVisual? to)
+        protected static Visual GetVisualParent(Visual? from, Visual? to)
         {
             var p1 = (from ?? to)!.VisualParent;
             var p2 = (to ?? from)!.VisualParent;

+ 1 - 1
src/Avalonia.Base/AttachedProperty.cs

@@ -34,7 +34,7 @@ namespace Avalonia
         /// </summary>
         /// <typeparam name="TOwner">The owner type.</typeparam>
         /// <returns>The property.</returns>
-        public new AttachedProperty<TValue> AddOwner<TOwner>() where TOwner : IAvaloniaObject
+        public new AttachedProperty<TValue> AddOwner<TOwner>() where TOwner : AvaloniaObject
         {
             AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), this);
             return this;

+ 0 - 1
src/Avalonia.Base/Avalonia.Base.csproj

@@ -42,7 +42,6 @@
     <InternalsVisibleTo Include="Avalonia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Win32, PublicKey=$(AvaloniaPublicKey)" />
-    <InternalsVisibleTo Include="Avalonia.Web.Blazor, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Dialogs, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" />

+ 1 - 1
src/Avalonia.Base/AvaloniaObject.cs

@@ -16,7 +16,7 @@ namespace Avalonia
     /// <remarks>
     /// This class is analogous to DependencyObject in WPF.
     /// </remarks>
-    public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged
+    public class AvaloniaObject : IAvaloniaObjectDebug, INotifyPropertyChanged
     {
         private readonly ValueStore _values;
         private AvaloniaObject? _inheritanceParent;

+ 38 - 62
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@@ -36,7 +36,7 @@ namespace Avalonia
         /// <remarks>
         /// The subscription to <paramref name="o"/> is created using a weak reference.
         /// </remarks>
-        public static IObservable<object?> GetObservable(this IAvaloniaObject o, AvaloniaProperty property)
+        public static IObservable<object?> GetObservable(this AvaloniaObject o, AvaloniaProperty property)
         {
             return new AvaloniaPropertyObservable<object?>(
                 o ?? throw new ArgumentNullException(nameof(o)), 
@@ -56,7 +56,7 @@ namespace Avalonia
         /// <remarks>
         /// The subscription to <paramref name="o"/> is created using a weak reference.
         /// </remarks>
-        public static IObservable<T> GetObservable<T>(this IAvaloniaObject o, AvaloniaProperty<T> property)
+        public static IObservable<T> GetObservable<T>(this AvaloniaObject o, AvaloniaProperty<T> property)
         {
             return new AvaloniaPropertyObservable<T>(
                 o ?? throw new ArgumentNullException(nameof(o)),
@@ -76,7 +76,7 @@ namespace Avalonia
         /// The subscription to <paramref name="o"/> is created using a weak reference.
         /// </remarks>
         public static IObservable<BindingValue<object?>> GetBindingObservable(
-            this IAvaloniaObject o,
+            this AvaloniaObject o,
             AvaloniaProperty property)
         {
             return new AvaloniaPropertyBindingObservable<object?>(
@@ -98,7 +98,7 @@ namespace Avalonia
         /// The subscription to <paramref name="o"/> is created using a weak reference.
         /// </remarks>
         public static IObservable<BindingValue<T>> GetBindingObservable<T>(
-            this IAvaloniaObject o,
+            this AvaloniaObject o,
             AvaloniaProperty<T> property)
         {
             return new AvaloniaPropertyBindingObservable<T>(
@@ -115,11 +115,11 @@ namespace Avalonia
         /// <param name="property">The property.</param>
         /// <returns>
         /// An observable which when subscribed pushes the property changed event args
-        /// each time a <see cref="IAvaloniaObject.PropertyChanged"/> event is raised
+        /// each time a <see cref="AvaloniaObject.PropertyChanged"/> event is raised
         /// for the specified property.
         /// </returns>
         public static IObservable<AvaloniaPropertyChangedEventArgs> GetPropertyChangedObservable(
-            this IAvaloniaObject o,
+            this AvaloniaObject o,
             AvaloniaProperty property)
         {
             return new AvaloniaPropertyChangedObservable(
@@ -140,7 +140,7 @@ namespace Avalonia
         /// property.
         /// </returns>
         public static ISubject<object?> GetSubject(
-            this IAvaloniaObject o,
+            this AvaloniaObject o,
             AvaloniaProperty property,
             BindingPriority priority = BindingPriority.LocalValue)
         {
@@ -163,7 +163,7 @@ namespace Avalonia
         /// property.
         /// </returns>
         public static ISubject<T> GetSubject<T>(
-            this IAvaloniaObject o,
+            this AvaloniaObject o,
             AvaloniaProperty<T> property,
             BindingPriority priority = BindingPriority.LocalValue)
         {
@@ -185,7 +185,7 @@ namespace Avalonia
         /// property.
         /// </returns>
         public static ISubject<BindingValue<object?>> GetBindingSubject(
-            this IAvaloniaObject o,
+            this AvaloniaObject o,
             AvaloniaProperty property,
             BindingPriority priority = BindingPriority.LocalValue)
         {
@@ -214,7 +214,7 @@ namespace Avalonia
         /// property.
         /// </returns>
         public static ISubject<BindingValue<T>> GetBindingSubject<T>(
-            this IAvaloniaObject o,
+            this AvaloniaObject o,
             AvaloniaProperty<T> property,
             BindingPriority priority = BindingPriority.LocalValue)
         {
@@ -241,7 +241,7 @@ namespace Avalonia
         /// A disposable which can be used to terminate the binding.
         /// </returns>
         public static IDisposable Bind<T>(
-            this IAvaloniaObject target,
+            this AvaloniaObject target,
             AvaloniaProperty<T> property,
             IObservable<BindingValue<T>> source,
             BindingPriority priority = BindingPriority.LocalValue)
@@ -250,17 +250,12 @@ namespace Avalonia
             property = property ?? throw new ArgumentNullException(nameof(property));
             source = source ?? throw new ArgumentNullException(nameof(source));
 
-            if (target is AvaloniaObject ao)
+            return property switch
             {
-                return property switch
-                {
-                    StyledPropertyBase<T> styled => ao.Bind(styled, source, priority),
-                    DirectPropertyBase<T> direct => ao.Bind(direct, source),
-                    _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
-                };
-            }
-
-            throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
+                StyledPropertyBase<T> styled => target.Bind(styled, source, priority),
+                DirectPropertyBase<T> direct => target.Bind(direct, source),
+                _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
+            };
         }
 
         /// <summary>
@@ -274,22 +269,17 @@ namespace Avalonia
         /// A disposable which can be used to terminate the binding.
         /// </returns>
         public static IDisposable Bind<T>(
-            this IAvaloniaObject target,
+            this AvaloniaObject target,
             AvaloniaProperty<T> property,
             IObservable<T> source,
             BindingPriority priority = BindingPriority.LocalValue)
         {
-            if (target is AvaloniaObject ao)
+            return property switch
             {
-                return property switch
-                {
-                    StyledPropertyBase<T> styled => ao.Bind(styled, source, priority),
-                    DirectPropertyBase<T> direct => ao.Bind(direct, source),
-                    _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
-                };
-            }
-
-            throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
+                StyledPropertyBase<T> styled => target.Bind(styled, source, priority),
+                DirectPropertyBase<T> direct => target.Bind(direct, source),
+                _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
+            };
         }
 
         /// <summary>
@@ -306,7 +296,7 @@ namespace Avalonia
         /// </param>
         /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
         public static IDisposable Bind(
-            this IAvaloniaObject target,
+            this AvaloniaObject target,
             AvaloniaProperty property,
             IBinding binding,
             object? anchor = null)
@@ -340,23 +330,17 @@ namespace Avalonia
         /// <param name="target">The object.</param>
         /// <param name="property">The property.</param>
         /// <returns>The value.</returns>
-        public static T GetValue<T>(this IAvaloniaObject target, AvaloniaProperty<T> property)
+        public static T GetValue<T>(this AvaloniaObject target, AvaloniaProperty<T> property)
         {
             target = target ?? throw new ArgumentNullException(nameof(target));
             property = property ?? throw new ArgumentNullException(nameof(property));
 
-            if (target is AvaloniaObject ao)
+            return property switch
             {
-                return property switch
-                {
-                    StyledPropertyBase<T> styled => ao.GetValue(styled),
-                    DirectPropertyBase<T> direct => ao.GetValue(direct),
-                    _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
-                };
-
-            }
-
-            throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
+                StyledPropertyBase<T> styled => target.GetValue(styled),
+                DirectPropertyBase<T> direct => target.GetValue(direct),
+                _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
+            };
         }
 
         /// <summary>
@@ -372,15 +356,13 @@ namespace Avalonia
         /// For direct properties returns the current value of the property.
         /// </remarks>
         public static object? GetBaseValue(
-            this IAvaloniaObject target,
+            this AvaloniaObject target,
             AvaloniaProperty property)
         {
             target = target ?? throw new ArgumentNullException(nameof(target));
             property = property ?? throw new ArgumentNullException(nameof(property));
 
-            if (target is AvaloniaObject ao)
-                return property.RouteGetBaseValue(ao);
-            throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
+            return property.RouteGetBaseValue(target);
         }
 
         /// <summary>
@@ -396,24 +378,18 @@ namespace Avalonia
         /// For direct properties returns the current value of the property.
         /// </remarks>
         public static Optional<T> GetBaseValue<T>(
-            this IAvaloniaObject target,
+            this AvaloniaObject target,
             AvaloniaProperty<T> property)
         {
             target = target ?? throw new ArgumentNullException(nameof(target));
             property = property ?? throw new ArgumentNullException(nameof(property));
 
-            if (target is AvaloniaObject ao)
+            return property switch
             {
-                return property switch
-                {
-                    StyledPropertyBase<T> styled => ao.GetBaseValue(styled),
-                    DirectPropertyBase<T> direct => ao.GetValue(direct),
-                    _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
-                };
-
-            }
-
-            throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
+                StyledPropertyBase<T> styled => target.GetBaseValue(styled),
+                DirectPropertyBase<T> direct => target.GetValue(direct),
+                _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
+            };
         }
 
         /// <summary>
@@ -474,7 +450,7 @@ namespace Avalonia
             }
 
             public InstancedBinding? Initiate(
-                IAvaloniaObject target,
+                AvaloniaObject target,
                 AvaloniaProperty? targetProperty,
                 object? anchor = null,
                 bool enableDataValidation = false)

+ 12 - 12
src/Avalonia.Base/AvaloniaProperty.cs

@@ -38,7 +38,7 @@ namespace Avalonia
             Type valueType,
             Type ownerType,
             AvaloniaPropertyMetadata metadata,
-            Action<IAvaloniaObject, bool>? notifying = null)
+            Action<AvaloniaObject, bool>? notifying = null)
         {
             _ = name ?? throw new ArgumentNullException(nameof(name));
 
@@ -145,7 +145,7 @@ namespace Avalonia
         /// will be true before the property change notifications are sent and false afterwards. This
         /// callback is intended to support Control.IsDataContextChanging.
         /// </remarks>
-        public Action<IAvaloniaObject, bool>? Notifying { get; }
+        public Action<AvaloniaObject, bool>? Notifying { get; }
 
         /// <summary>
         /// Gets the integer ID that represents this property.
@@ -177,7 +177,7 @@ namespace Avalonia
         {
             return new IndexerDescriptor
             {
-                Priority = BindingPriority.TemplatedParent,
+                Priority = BindingPriority.Template,
                 Property = property,
             };
         }
@@ -238,9 +238,9 @@ namespace Avalonia
             bool inherits = false,
             BindingMode defaultBindingMode = BindingMode.OneWay,
             Func<TValue, bool>? validate = null,
-            Func<IAvaloniaObject, TValue, TValue>? coerce = null,
-            Action<IAvaloniaObject, bool>? notifying = null)
-                where TOwner : IAvaloniaObject
+            Func<AvaloniaObject, TValue, TValue>? coerce = null,
+            Action<AvaloniaObject, bool>? notifying = null)
+                where TOwner : AvaloniaObject
         {
             _ = name ?? throw new ArgumentNullException(nameof(name));
 
@@ -279,8 +279,8 @@ namespace Avalonia
             bool inherits = false,
             BindingMode defaultBindingMode = BindingMode.OneWay,
             Func<TValue, bool>? validate = null,
-            Func<IAvaloniaObject, TValue, TValue>? coerce = null)
-                where THost : IAvaloniaObject
+            Func<AvaloniaObject, TValue, TValue>? coerce = null)
+                where THost : AvaloniaObject
         {
             _ = name ?? throw new ArgumentNullException(nameof(name));
 
@@ -316,8 +316,8 @@ namespace Avalonia
             bool inherits = false,
             BindingMode defaultBindingMode = BindingMode.OneWay,
             Func<TValue, bool>? validate = null,
-            Func<IAvaloniaObject, TValue, TValue>? coerce = null)
-                where THost : IAvaloniaObject
+            Func<AvaloniaObject, TValue, TValue>? coerce = null)
+                where THost : AvaloniaObject
         {
             _ = name ?? throw new ArgumentNullException(nameof(name));
 
@@ -354,7 +354,7 @@ namespace Avalonia
             TValue unsetValue = default!,
             BindingMode defaultBindingMode = BindingMode.OneWay,
             bool enableDataValidation = false)
-                where TOwner : IAvaloniaObject
+                where TOwner : AvaloniaObject
         {
             _ = name ?? throw new ArgumentNullException(nameof(name));
             _ = getter ?? throw new ArgumentNullException(nameof(getter));
@@ -415,7 +415,7 @@ namespace Avalonia
         /// <returns>
         /// The property metadata.
         /// </returns>
-        public AvaloniaPropertyMetadata GetMetadata<T>() where T : IAvaloniaObject
+        public AvaloniaPropertyMetadata GetMetadata<T>() where T : AvaloniaObject
         {
             return GetMetadata(typeof(T));
         }

+ 3 - 3
src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs

@@ -9,7 +9,7 @@ namespace Avalonia
     public abstract class AvaloniaPropertyChangedEventArgs : EventArgs
     {
         public AvaloniaPropertyChangedEventArgs(
-            IAvaloniaObject sender,
+            AvaloniaObject sender,
             BindingPriority priority)
         {
             Sender = sender;
@@ -18,7 +18,7 @@ namespace Avalonia
         }
 
         internal AvaloniaPropertyChangedEventArgs(
-            IAvaloniaObject sender,
+            AvaloniaObject sender,
             BindingPriority priority,
             bool isEffectiveValueChange)
         {
@@ -31,7 +31,7 @@ namespace Avalonia
         /// Gets the <see cref="AvaloniaObject"/> that the property changed on.
         /// </summary>
         /// <value>The sender object.</value>
-        public IAvaloniaObject Sender { get; }
+        public AvaloniaObject Sender { get; }
 
         /// <summary>
         /// Gets the property that changed.

+ 2 - 2
src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs

@@ -16,7 +16,7 @@ namespace Avalonia
         /// <param name="newValue">The new value of the property.</param>
         /// <param name="priority">The priority of the binding that produced the value.</param>
         public AvaloniaPropertyChangedEventArgs(
-            IAvaloniaObject sender,
+            AvaloniaObject sender,
             AvaloniaProperty<T> property,
             Optional<T> oldValue,
             BindingValue<T> newValue,
@@ -26,7 +26,7 @@ namespace Avalonia
         }
 
         internal AvaloniaPropertyChangedEventArgs(
-            IAvaloniaObject sender,
+            AvaloniaObject sender,
             AvaloniaProperty<T> property,
             Optional<T> oldValue,
             BindingValue<T> newValue,

+ 6 - 6
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@@ -189,7 +189,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="o">The object.</param>
         /// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
-        public IReadOnlyList<AvaloniaProperty> GetRegistered(IAvaloniaObject o)
+        public IReadOnlyList<AvaloniaProperty> GetRegistered(AvaloniaObject o)
         {
             _ = o ?? throw new ArgumentNullException(nameof(o));
 
@@ -205,7 +205,7 @@ namespace Avalonia
         /// The registered.
         /// </returns>
         public DirectPropertyBase<T> GetRegisteredDirect<T>(
-            IAvaloniaObject o,
+            AvaloniaObject o,
             DirectPropertyBase<T> property)
         {
             return FindRegisteredDirect(o, property) ??
@@ -260,7 +260,7 @@ namespace Avalonia
         /// <exception cref="InvalidOperationException">
         /// The property name contains a '.'.
         /// </exception>
-        public AvaloniaProperty? FindRegistered(IAvaloniaObject o, string name)
+        public AvaloniaProperty? FindRegistered(AvaloniaObject o, string name)
         {
             _ = o ?? throw new ArgumentNullException(nameof(o));
             _ = name ?? throw new ArgumentNullException(nameof(name));
@@ -277,7 +277,7 @@ namespace Avalonia
         /// The registered property or null if no matching property found.
         /// </returns>
         public DirectPropertyBase<T>? FindRegisteredDirect<T>(
-            IAvaloniaObject o,
+            AvaloniaObject o,
             DirectPropertyBase<T> property)
         {
             if (property.Owner == o.GetType())
@@ -362,7 +362,7 @@ namespace Avalonia
         /// <param name="property">The property.</param>
         /// <remarks>
         /// You won't usually want to call this method directly, instead use the
-        /// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{IAvaloniaObject, TValue, TValue}, Action{IAvaloniaObject, bool})"/>
+        /// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{AvaloniaObject, TValue, TValue}, Action{AvaloniaObject, bool})"/>
         /// method.
         /// </remarks>
         public void Register(Type type, AvaloniaProperty property)
@@ -413,7 +413,7 @@ namespace Avalonia
         /// <param name="property">The property.</param>
         /// <remarks>
         /// You won't usually want to call this method directly, instead use the
-        /// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{IAvaloniaObject, TValue, TValue})"/>
+        /// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{AvaloniaObject, TValue, TValue})"/>
         /// method.
         /// </remarks>
         public void RegisterAttached(Type type, AvaloniaProperty property)

+ 1 - 1
src/Avalonia.Base/AvaloniaProperty`1.cs

@@ -24,7 +24,7 @@ namespace Avalonia
             string name,
             Type ownerType,
             AvaloniaPropertyMetadata metadata,
-            Action<IAvaloniaObject, bool>? notifying = null)
+            Action<AvaloniaObject, bool>? notifying = null)
             : base(name, typeof(TValue), ownerType, metadata, notifying)
         {
             _changed = new Subject<AvaloniaPropertyChangedEventArgs<TValue>>();

+ 2 - 2
src/Avalonia.Base/ClassBindingManager.cs

@@ -9,7 +9,7 @@ namespace Avalonia
         private static readonly Dictionary<string, AvaloniaProperty> s_RegisteredProperties =
             new Dictionary<string, AvaloniaProperty>();
         
-        public static IDisposable Bind(IStyledElement target, string className, IBinding source, object anchor)
+        public static IDisposable Bind(StyledElement target, string className, IBinding source, object anchor)
         {
             if (!s_RegisteredProperties.TryGetValue(className, out var prop))
                 s_RegisteredProperties[className] = prop = RegisterClassProxyProperty(className);
@@ -21,7 +21,7 @@ namespace Avalonia
             var prop = AvaloniaProperty.Register<StyledElement, bool>("__AvaloniaReserved::Classes::" + className);
             prop.Changed.Subscribe(args =>
             {
-                var classes = ((IStyledElement)args.Sender).Classes;
+                var classes = ((StyledElement)args.Sender).Classes;
                 classes.Set(className, args.NewValue.GetValueOrDefault());
             });
             

+ 1 - 1
src/Avalonia.Base/Controls/Classes.cs

@@ -6,7 +6,7 @@ using Avalonia.Utilities;
 namespace Avalonia.Controls
 {
     /// <summary>
-    /// Holds a collection of style classes for an <see cref="IStyledElement"/>.
+    /// Holds a collection of style classes for an <see cref="StyledElement"/>.
     /// </summary>
     /// <remarks>
     /// Similar to CSS, each control may have any number of styling classes applied.

+ 1 - 1
src/Avalonia.Base/Controls/ISetInheritanceParent.cs

@@ -17,6 +17,6 @@ namespace Avalonia.Controls
         /// Sets the control's inheritance parent.
         /// </summary>
         /// <param name="parent">The parent.</param>
-        void SetParent(IAvaloniaObject? parent);
+        void SetParent(AvaloniaObject? parent);
     }
 }

+ 2 - 1
src/Avalonia.Base/Controls/ResourceNodeExtensions.cs

@@ -2,6 +2,7 @@
 using Avalonia.Data.Converters;
 using Avalonia.LogicalTree;
 using Avalonia.Reactive;
+using Avalonia.Styling;
 
 #nullable enable
 
@@ -49,7 +50,7 @@ namespace Avalonia.Controls
                     return true;
                 }
 
-                current = (current as IStyledElement)?.StylingParent as IResourceNode;
+                current = (current as IStyleHost)?.StylingParent as IResourceNode;
             }
 
             value = null;

+ 2 - 2
src/Avalonia.Base/Data/BindingOperations.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Data
         public static readonly object DoNothing = new DoNothingType();
 
         /// <summary>
-        /// Applies an <see cref="InstancedBinding"/> a property on an <see cref="IAvaloniaObject"/>.
+        /// Applies an <see cref="InstancedBinding"/> a property on an <see cref="AvaloniaObject"/>.
         /// </summary>
         /// <param name="target">The target object.</param>
         /// <param name="property">The property to bind.</param>
@@ -22,7 +22,7 @@ namespace Avalonia.Data
         /// </param>
         /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
         public static IDisposable Apply(
-            IAvaloniaObject target,
+            AvaloniaObject target,
             AvaloniaProperty property,
             InstancedBinding binding,
             object? anchor)

+ 13 - 9
src/Avalonia.Base/Data/BindingPriority.cs

@@ -1,7 +1,9 @@
+using System;
+
 namespace Avalonia.Data
 {
     /// <summary>
-    /// The priority of a binding.
+    /// The priority of a value or binding.
     /// </summary>
     public enum BindingPriority
     {
@@ -16,23 +18,22 @@ namespace Avalonia.Data
         LocalValue = 0,
 
         /// <summary>
-        /// A triggered style binding.
+        /// A triggered style value.
         /// </summary>
         /// <remarks>
         /// A style trigger is a selector such as .class which overrides a
-        /// <see cref="TemplatedParent"/> binding. In this way, a basic control can have
-        /// for example a Background from the templated parent which changes when the
-        /// control has the :pointerover class.
+        /// <see cref="Template"/> value. In this way, a control can have, e.g. a Background from
+        /// the template which changes when the control has the :pointerover class.
         /// </remarks>
         StyleTrigger,
 
         /// <summary>
-        /// A binding to a property on the templated parent.
+        /// A value from the control's template.
         /// </summary>
-        TemplatedParent,
+        Template,
 
         /// <summary>
-        /// A style binding.
+        /// A style value.
         /// </summary>
         Style,
         
@@ -42,8 +43,11 @@ namespace Avalonia.Data
         Inherited,
 
         /// <summary>
-        /// The binding is uninitialized.
+        /// The value is uninitialized.
         /// </summary>
         Unset = int.MaxValue,
+
+        [Obsolete("Use Template priority")]
+        TemplatedParent = Template,
     }
 }

+ 2 - 2
src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs

@@ -24,7 +24,7 @@ namespace Avalonia.Data.Core
         {
             try
             {
-                if (Target.TryGetTarget(out var target) && target is IAvaloniaObject obj)
+                if (Target.TryGetTarget(out var target) && target is AvaloniaObject obj)
                 {
                     obj.SetValue(_property, value, priority);
                     return true;
@@ -39,7 +39,7 @@ namespace Avalonia.Data.Core
 
         protected override void StartListeningCore(WeakReference<object?> reference)
         {
-            if (reference.TryGetTarget(out var target) && target is IAvaloniaObject obj)
+            if (reference.TryGetTarget(out var target) && target is AvaloniaObject obj)
             {
                 _subscription = new AvaloniaPropertyObservable<object?>(obj, _property).Subscribe(ValueChanged);
             }

+ 1 - 1
src/Avalonia.Base/Data/IBinding.cs

@@ -24,7 +24,7 @@ namespace Avalonia.Data
         /// A <see cref="InstancedBinding"/> or null if the binding could not be resolved.
         /// </returns>
         InstancedBinding? Initiate(
-            IAvaloniaObject target, 
+            AvaloniaObject target, 
             AvaloniaProperty? targetProperty,
             object? anchor = null,
             bool enableDataValidation = false);

+ 3 - 3
src/Avalonia.Base/Data/IndexerBinding.cs

@@ -3,7 +3,7 @@
     public class IndexerBinding : IBinding
     {
         public IndexerBinding(
-            IAvaloniaObject source,
+            AvaloniaObject source,
             AvaloniaProperty property,
             BindingMode mode)
         {
@@ -12,12 +12,12 @@
             Mode = mode;
         }
 
-        private IAvaloniaObject Source { get; }
+        private AvaloniaObject Source { get; }
         public AvaloniaProperty Property { get; }
         private BindingMode Mode { get; }
 
         public InstancedBinding? Initiate(
-            IAvaloniaObject target,
+            AvaloniaObject target,
             AvaloniaProperty? targetProperty,
             object? anchor = null,
             bool enableDataValidation = false)

+ 1 - 1
src/Avalonia.Base/Data/InstancedBinding.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Data
     /// <remarks>
     /// Whereas an <see cref="IBinding"/> holds a description of a binding such as "Bind to the X
     /// property on a control's DataContext"; this class represents a binding that has been 
-    /// *instanced* by calling <see cref="IBinding.Initiate(IAvaloniaObject, AvaloniaProperty, object, bool)"/>
+    /// *instanced* by calling <see cref="IBinding.Initiate(AvaloniaObject, AvaloniaProperty, object, bool)"/>
     /// on a target object.
     /// </remarks>
     public class InstancedBinding

+ 1 - 1
src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Diagnostics
     public interface IAvaloniaObjectDebug
     {
         /// <summary>
-        /// Gets the subscriber list for the <see cref="IAvaloniaObject.PropertyChanged"/>
+        /// Gets the subscriber list for the <see cref="AvaloniaObject.PropertyChanged"/>
         /// event.
         /// </summary>
         /// <returns>

+ 5 - 5
src/Avalonia.Base/DirectProperty.cs

@@ -15,7 +15,7 @@ namespace Avalonia
     /// allows the avalonia property system to read and write the current value.
     /// </remarks>
     public class DirectProperty<TOwner, TValue> : DirectPropertyBase<TValue>, IDirectPropertyAccessor
-        where TOwner : IAvaloniaObject
+        where TOwner : AvaloniaObject
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="DirectProperty{TOwner, TValue}"/> class.
@@ -151,13 +151,13 @@ namespace Avalonia
         }
 
         /// <inheritdoc/>
-        internal override TValue InvokeGetter(IAvaloniaObject instance)
+        internal override TValue InvokeGetter(AvaloniaObject instance)
         {
             return Getter((TOwner)instance);
         }
 
         /// <inheritdoc/>
-        internal override void InvokeSetter(IAvaloniaObject instance, BindingValue<TValue> value)
+        internal override void InvokeSetter(AvaloniaObject instance, BindingValue<TValue> value)
         {
             if (Setter == null)
             {
@@ -171,13 +171,13 @@ namespace Avalonia
         }
 
         /// <inheritdoc/>
-        object? IDirectPropertyAccessor.GetValue(IAvaloniaObject instance)
+        object? IDirectPropertyAccessor.GetValue(AvaloniaObject instance)
         {
             return Getter((TOwner)instance);
         }
 
         /// <inheritdoc/>
-        void IDirectPropertyAccessor.SetValue(IAvaloniaObject instance, object? value)
+        void IDirectPropertyAccessor.SetValue(AvaloniaObject instance, object? value)
         {
             if (Setter == null)
             {

+ 3 - 3
src/Avalonia.Base/DirectPropertyBase.cs

@@ -54,14 +54,14 @@ namespace Avalonia
         /// </summary>
         /// <param name="instance">The instance.</param>
         /// <returns>The property value.</returns>
-        internal abstract TValue InvokeGetter(IAvaloniaObject instance);
+        internal abstract TValue InvokeGetter(AvaloniaObject instance);
 
         /// <summary>
         /// Sets the value of the property on the instance.
         /// </summary>
         /// <param name="instance">The instance.</param>
         /// <param name="value">The value.</param>
-        internal abstract void InvokeSetter(IAvaloniaObject instance, BindingValue<TValue> value);
+        internal abstract void InvokeSetter(AvaloniaObject instance, BindingValue<TValue> value);
 
         /// <summary>
         /// Gets the unset value for the property on the specified type.
@@ -91,7 +91,7 @@ namespace Avalonia
         /// </summary>
         /// <typeparam name="T">The type.</typeparam>
         /// <param name="metadata">The metadata.</param>
-        public void OverrideMetadata<T>(DirectPropertyMetadata<TValue> metadata) where T : IAvaloniaObject
+        public void OverrideMetadata<T>(DirectPropertyMetadata<TValue> metadata) where T : AvaloniaObject
         {
             base.OverrideMetadata(typeof(T), metadata);
         }

+ 0 - 79
src/Avalonia.Base/IAvaloniaObject.cs

@@ -1,79 +0,0 @@
-using System;
-using Avalonia.Data;
-using Avalonia.Metadata;
-
-namespace Avalonia
-{
-    /// <summary>
-    /// Interface for getting/setting <see cref="AvaloniaProperty"/> values on an object.
-    /// </summary>
-    [NotClientImplementable]
-    public interface IAvaloniaObject
-    {
-        /// <summary>
-        /// Raised when a <see cref="AvaloniaProperty"/> value changes on this object.
-        /// </summary>
-        event EventHandler<AvaloniaPropertyChangedEventArgs>? PropertyChanged;
-
-        /// <summary>
-        /// Clears an <see cref="AvaloniaProperty"/>'s local value.
-        /// </summary>
-        /// <param name="property">The property.</param>
-        void ClearValue(AvaloniaProperty property);
-
-        /// <summary>
-        /// Gets a <see cref="AvaloniaProperty"/> value.
-        /// </summary>
-        /// <param name="property">The property.</param>
-        /// <returns>The value.</returns>
-        object? GetValue(AvaloniaProperty property);
-
-        /// <summary>
-        /// Checks whether a <see cref="AvaloniaProperty"/> is animating.
-        /// </summary>
-        /// <param name="property">The property.</param>
-        /// <returns>True if the property is animating, otherwise false.</returns>
-        bool IsAnimating(AvaloniaProperty property);
-
-        /// <summary>
-        /// Checks whether a <see cref="AvaloniaProperty"/> is set on this object.
-        /// </summary>
-        /// <param name="property">The property.</param>
-        /// <returns>True if the property is set, otherwise false.</returns>
-        bool IsSet(AvaloniaProperty property);
-
-        /// <summary>
-        /// Sets a <see cref="AvaloniaProperty"/> value.
-        /// </summary>
-        /// <param name="property">The property.</param>
-        /// <param name="value">The value.</param>
-        /// <param name="priority">The priority of the value.</param>
-        /// <returns>
-        /// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
-        /// </returns>
-        IDisposable? SetValue(
-            AvaloniaProperty property,
-            object? value,
-            BindingPriority priority = BindingPriority.LocalValue);
-
-        /// <summary>
-        /// Binds a <see cref="AvaloniaProperty"/> to an observable.
-        /// </summary>
-        /// <param name="property">The property.</param>
-        /// <param name="source">The observable.</param>
-        /// <param name="priority">The priority of the binding.</param>
-        /// <returns>
-        /// A disposable which can be used to terminate the binding.
-        /// </returns>
-        IDisposable Bind(
-            AvaloniaProperty property,
-            IObservable<object?> source,
-            BindingPriority priority = BindingPriority.LocalValue);
-
-        /// <summary>
-        /// Coerces the specified <see cref="AvaloniaProperty"/>.
-        /// </summary>
-        /// <param name="property">The property.</param>
-        void CoerceValue(AvaloniaProperty property);
-    }
-}

+ 1 - 1
src/Avalonia.Base/IDataContextProvider.cs

@@ -6,7 +6,7 @@ namespace Avalonia
     /// Defines an element with a data context that can be used for binding.
     /// </summary>
     [NotClientImplementable]
-    public interface IDataContextProvider : IAvaloniaObject
+    public interface IDataContextProvider
     {
         /// <summary>
         /// Gets or sets the element's data context.

+ 2 - 2
src/Avalonia.Base/IDirectPropertyAccessor.cs

@@ -24,13 +24,13 @@ namespace Avalonia
         /// </summary>
         /// <param name="instance">The instance.</param>
         /// <returns>The property value.</returns>
-        object? GetValue(IAvaloniaObject instance);
+        object? GetValue(AvaloniaObject instance);
 
         /// <summary>
         /// Sets the value of the property on the instance.
         /// </summary>
         /// <param name="instance">The instance.</param>
         /// <param name="value">The value.</param>
-        void SetValue(IAvaloniaObject instance, object? value);
+        void SetValue(AvaloniaObject instance, object? value);
     }
 }

+ 0 - 39
src/Avalonia.Base/IStyledElement.cs

@@ -1,39 +0,0 @@
-using System;
-using System.ComponentModel;
-using Avalonia.Controls;
-using Avalonia.LogicalTree;
-using Avalonia.Metadata;
-using Avalonia.Styling;
-
-namespace Avalonia
-{
-    [NotClientImplementable]
-    public interface IStyledElement :
-        IStyleable,
-        IStyleHost,
-        ILogical,
-        IResourceHost,
-        IDataContextProvider,
-        ISupportInitialize
-    {
-        /// <summary>
-        /// Occurs when the control has finished initialization.
-        /// </summary>
-        event EventHandler? Initialized;
-
-        /// <summary>
-        /// Gets a value that indicates whether the element has finished initialization.
-        /// </summary>
-        bool IsInitialized { get; }
-
-        /// <summary>
-        /// Gets or sets the control's styling classes.
-        /// </summary>
-        new Classes Classes { get; set; }
-
-        /// <summary>
-        /// Gets the control's logical parent.
-        /// </summary>
-        IStyledElement? Parent { get; }
-    }
-}

+ 2 - 2
src/Avalonia.Base/Input/AccessKeyHandler.cs

@@ -182,13 +182,13 @@ namespace Avalonia.Input
                 // find all controls who have registered that access key.
                 var text = e.Key.ToString().ToUpper();
                 var matches = _registered
-                    .Where(x => x.Item1 == text && x.Item2.IsEffectivelyVisible)
+                    .Where(x => x.Item1 == text && ((Visual)x.Item2).IsEffectivelyVisible)
                     .Select(x => x.Item2);
 
                 // If the menu is open, only match controls in the menu's visual tree.
                 if (menuIsOpen)
                 {
-                    matches = matches.Where(x => x is not null && MainMenu!.IsVisualAncestorOf(x));
+                    matches = matches.Where(x => x is not null && ((Visual)MainMenu!).IsVisualAncestorOf((Visual)x));
                 }
 
                 var match = matches.FirstOrDefault();

+ 3 - 2
src/Avalonia.Base/Input/DragDropDevice.cs

@@ -13,7 +13,8 @@ namespace Avalonia.Input
         
         private static Interactive? GetTarget(IInputRoot root, Point local)
         {
-            var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault();
+            var hit = root.InputHitTest(local) as Visual;
+            var target = hit?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault();
             if (target != null && DragDrop.GetAllowDrop(target))
                 return target;
             return null;
@@ -24,7 +25,7 @@ namespace Avalonia.Input
             if (target == null)
                 return DragDropEffects.None;
 
-            var p = inputRoot.TranslatePoint(point, target);
+            var p = ((Visual)inputRoot).TranslatePoint(point, target);
 
             if (!p.HasValue)
                 return DragDropEffects.None;

+ 1 - 1
src/Avalonia.Base/Input/DragEventArgs.cs

@@ -15,7 +15,7 @@ namespace Avalonia.Input
 
         public KeyModifiers KeyModifiers { get; private set; }
 
-        public Point GetPosition(IVisual relativeTo)
+        public Point GetPosition(Visual relativeTo)
         {
             var point = new Point(0, 0);
 

+ 10 - 7
src/Avalonia.Base/Input/FocusManager.cs

@@ -185,7 +185,7 @@ namespace Avalonia.Input
         /// </summary>
         /// <param name="e">The element.</param>
         /// <returns>True if the element can be focused.</returns>
-        private static bool CanFocus(IInputElement e) => e.Focusable && e.IsEffectivelyEnabled && e.IsVisible;
+        private static bool CanFocus(IInputElement e) => e.Focusable && e.IsEffectivelyEnabled && IsVisible(e);
 
         /// <summary>
         /// Gets the focus scope ancestors of the specified control, traversing popups.
@@ -198,14 +198,15 @@ namespace Avalonia.Input
 
             while (c != null)
             {
-                var scope = c as IFocusScope;
-
-                if (scope != null && c.VisualRoot?.IsVisible == true)
+                if (c is IFocusScope scope &&
+                    c is Visual v &&
+                    v.VisualRoot is Visual root &&
+                    root.IsVisible)
                 {
                     yield return scope;
                 }
 
-                c = c.GetVisualParent<IInputElement>() ??
+                c = (c as Visual)?.GetVisualParent<IInputElement>() ??
                     ((c as IHostedVisualTreeRoot)?.Host as IInputElement);
             }
         }
@@ -221,11 +222,11 @@ namespace Avalonia.Input
                 return;
 
             var ev = (PointerPressedEventArgs)e;
-            var visual = (IVisual)sender;
+            var visual = (Visual)sender;
 
             if (sender == e.Source && ev.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
             {
-                IVisual? element = ev.Pointer?.Captured ?? e.Source as IInputElement;
+                Visual? element = ev.Pointer?.Captured as Visual ?? e.Source as Visual;
 
                 while (element != null)
                 {
@@ -240,5 +241,7 @@ namespace Avalonia.Input
                 }
             }
         }
+
+        private static bool IsVisible(IInputElement e) => (e as Visual)?.IsVisible ?? true;
     }
 }

+ 2 - 2
src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs

@@ -35,8 +35,8 @@ namespace Avalonia.Input.GestureRecognizers
             if (_inputElement is ILogical logicalParent && recognizer is ISetLogicalParent logical)
             {
                 logical.SetParent(logicalParent);
-                if (recognizer is IStyleable styleableRecognizer
-                    && _inputElement is IStyleable styleableParent)
+                if (recognizer is StyledElement styleableRecognizer
+                    && _inputElement is StyledElement styleableParent)
                     styleableRecognizer.Bind(StyledElement.TemplatedParentProperty,
                         styleableParent.GetObservable(StyledElement.TemplatedParentProperty));
             }

+ 2 - 2
src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs

@@ -72,7 +72,7 @@ namespace Avalonia.Input.GestureRecognizers
                 EndGesture();
                 _tracking = e.Pointer;
                 _gestureId = ScrollGestureEventArgs.GetNextFreeId();
-                _trackedRootPoint = e.GetPosition(_target);
+                _trackedRootPoint = e.GetPosition((Visual?)_target);
             }
         }
 
@@ -86,7 +86,7 @@ namespace Avalonia.Input.GestureRecognizers
         {
             if (e.Pointer == _tracking)
             {
-                var rootPoint = e.GetPosition(_target);
+                var rootPoint = e.GetPosition((Visual?)_target);
                 if (!_scrolling)
                 {
                     if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance)

+ 18 - 15
src/Avalonia.Base/Input/Gestures.cs

@@ -43,7 +43,7 @@ namespace Avalonia.Input
             RoutedEvent.Register<PointerDeltaEventArgs>(
                 "PointerSwipeGesture", RoutingStrategies.Bubble, typeof(Gestures));
 
-        private static readonly WeakReference<IInteractive?> s_lastPress = new WeakReference<IInteractive?>(null);
+        private static readonly WeakReference<object?> s_lastPress = new WeakReference<object?>(null);
         private static Point s_lastPressPoint;
 
         static Gestures()
@@ -52,32 +52,32 @@ namespace Avalonia.Input
             InputElement.PointerReleasedEvent.RouteFinished.Subscribe(PointerReleased);
         }
 
-        public static void AddTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void AddTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
             element.AddHandler(TappedEvent, handler);
         }
 
-        public static void AddDoubleTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void AddDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
             element.AddHandler(DoubleTappedEvent, handler);
         }
 
-        public static void AddRightTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void AddRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
             element.AddHandler(RightTappedEvent, handler);
         }
 
-        public static void RemoveTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void RemoveTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
             element.RemoveHandler(TappedEvent, handler);
         }
 
-        public static void RemoveDoubleTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void RemoveDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
             element.RemoveHandler(DoubleTappedEvent, handler);
         }
 
-        public static void RemoveRightTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
+        public static void RemoveRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
         {
             element.RemoveHandler(RightTappedEvent, handler);
         }
@@ -92,20 +92,22 @@ namespace Avalonia.Input
             if (ev.Route == RoutingStrategies.Bubble)
             {
                 var e = (PointerPressedEventArgs)ev;
-                var visual = (IVisual)ev.Source;
+                var visual = (Visual)ev.Source;
 
                 if (e.ClickCount % 2 == 1)
                 {
                     s_isDoubleTapped = false;
                     s_lastPress.SetTarget(ev.Source);
-                    s_lastPressPoint = e.GetPosition((IVisual)ev.Source);
+                    s_lastPressPoint = e.GetPosition((Visual)ev.Source);
                 }
                 else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
                 {
-                    if (s_lastPress.TryGetTarget(out var target) && target == e.Source)
+                    if (s_lastPress.TryGetTarget(out var target) && 
+                        target == e.Source && 
+                        e.Source is Interactive i)
                     {
                         s_isDoubleTapped = true;
-                        e.Source.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e));
+                        i.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e));
                     }
                 }
             }
@@ -119,9 +121,10 @@ namespace Avalonia.Input
 
                 if (s_lastPress.TryGetTarget(out var target) && 
                     target == e.Source &&
-                    e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right)
+                    e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right &&
+                    e.Source is Interactive i)
                 {
-                    var point = e.GetCurrentPoint((IVisual)target);
+                    var point = e.GetCurrentPoint((Visual)target);
                     var settings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
                     var tapSize = settings?.GetTapSize(point.Pointer.Type) ?? new Size(4, 4);
                     var tapRect = new Rect(s_lastPressPoint, new Size())
@@ -131,13 +134,13 @@ namespace Avalonia.Input
                     {
                         if (e.InitialPressMouseButton == MouseButton.Right)
                         {
-                            e.Source.RaiseEvent(new TappedEventArgs(RightTappedEvent, e));
+                            i.RaiseEvent(new TappedEventArgs(RightTappedEvent, e));
                         }
                         //s_isDoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called.
                         //This behaviour matches UWP behaviour.
                         else if (s_isDoubleTapped == false)
                         {
-                            e.Source.RaiseEvent(new TappedEventArgs(TappedEvent, e));
+                            i.RaiseEvent(new TappedEventArgs(TappedEvent, e));
                         }
                     }
                 }

+ 34 - 5
src/Avalonia.Base/Input/IInputElement.cs

@@ -2,9 +2,6 @@ using System;
 using System.Collections.Generic;
 using Avalonia.Interactivity;
 using Avalonia.Metadata;
-using Avalonia.VisualTree;
-
-#nullable enable
 
 namespace Avalonia.Input
 {
@@ -12,7 +9,7 @@ namespace Avalonia.Input
     /// Defines input-related functionality for a control.
     /// </summary>
     [NotClientImplementable]
-    public interface IInputElement : IInteractive, IVisual
+    public interface IInputElement
     {
         /// <summary>
         /// Occurs when the control receives focus.
@@ -93,7 +90,12 @@ namespace Avalonia.Input
         /// <see cref="IsEnabled"/> value of this control and its parent controls.
         /// </remarks>
         bool IsEffectivelyEnabled { get; }
-        
+
+        /// <summary>
+        /// Gets a value indicating whether this control and all its parents are visible.
+        /// </summary>
+        bool IsEffectivelyVisible { get; }
+
         /// <summary>
         /// Gets a value indicating whether keyboard focus is anywhere within the element or its visual tree child elements.
         /// </summary>
@@ -123,5 +125,32 @@ namespace Avalonia.Input
         /// Gets the key bindings for the element.
         /// </summary>
         List<KeyBinding> KeyBindings { get; }
+
+        /// <summary>
+        /// Adds a handler for the specified routed event.
+        /// </summary>
+        /// <param name="routedEvent">The routed event.</param>
+        /// <param name="handler">The handler.</param>
+        /// <param name="routes">The routing strategies to listen to.</param>
+        /// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
+        /// <returns>A disposable that terminates the event subscription.</returns>
+        void AddHandler(
+            RoutedEvent routedEvent,
+            Delegate handler,
+            RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
+            bool handledEventsToo = false);
+
+        /// <summary>
+        /// Removes a handler for the specified routed event.
+        /// </summary>
+        /// <param name="routedEvent">The routed event.</param>
+        /// <param name="handler">The handler.</param>
+        void RemoveHandler(RoutedEvent routedEvent, Delegate handler);
+
+        /// <summary>
+        /// Raises a routed event.
+        /// </summary>
+        /// <param name="e">The event args.</param>
+        void RaiseEvent(RoutedEventArgs e);
     }
 }

+ 1 - 1
src/Avalonia.Base/Input/IMainMenu.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Input
     /// Defines the interface for a window's main menu.
     /// </summary>
     [NotClientImplementable]
-    public interface IMainMenu : IVisual
+    public interface IMainMenu
     {
         /// <summary>
         /// Gets a value indicating whether the menu is open.

+ 9 - 8
src/Avalonia.Base/Input/InputExtensions.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Input
     /// </summary>
     public static class InputExtensions
     {
-        private static readonly Func<IVisual, bool> s_hitTestDelegate = IsHitTestVisible;
+        private static readonly Func<Visual, bool> s_hitTestDelegate = IsHitTestVisible;
 
         /// <summary>
         /// Returns the active input elements at a point on an <see cref="IInputElement"/>.
@@ -26,7 +26,8 @@ namespace Avalonia.Input
         {
             element = element ?? throw new ArgumentNullException(nameof(element));
 
-            return element.GetVisualsAt(p, s_hitTestDelegate).Cast<IInputElement>();
+            return (element as Visual)?.GetVisualsAt(p, s_hitTestDelegate).Cast<IInputElement>() ??
+                Enumerable.Empty<IInputElement>();
         }
 
         /// <summary>
@@ -39,7 +40,7 @@ namespace Avalonia.Input
         {
             element = element ?? throw new ArgumentNullException(nameof(element));
 
-            return element.GetVisualAt(p, s_hitTestDelegate) as IInputElement;
+            return (element as Visual)?.GetVisualAt(p, s_hitTestDelegate) as IInputElement;
         }
 
         /// <summary>
@@ -55,22 +56,22 @@ namespace Avalonia.Input
         public static IInputElement? InputHitTest(
             this IInputElement element,
             Point p,
-            Func<IVisual, bool> filter)
+            Func<Visual, bool> filter)
         {
             element = element ?? throw new ArgumentNullException(nameof(element));
             filter = filter ?? throw new ArgumentNullException(nameof(filter));
 
-            return element.GetVisualAt(p, x => s_hitTestDelegate(x) && filter(x)) as IInputElement;
+            return (element as Visual)?.GetVisualAt(p, x => s_hitTestDelegate(x) && filter(x)) as IInputElement;
         }
 
-        private static bool IsHitTestVisible(IVisual visual)
+        private static bool IsHitTestVisible(Visual visual)
         {
             var element = visual as IInputElement;
             return element != null &&
-                   element.IsVisible &&
+                   visual.IsVisible &&
                    element.IsHitTestVisible &&
                    element.IsEffectivelyEnabled &&
-                   element.IsAttachedToVisualTree;
+                   visual.IsAttachedToVisualTree;
         }
     }
 }

+ 24 - 19
src/Avalonia.Base/Input/KeyboardDevice.cs

@@ -3,7 +3,6 @@ using System.Runtime.CompilerServices;
 using Avalonia.Input.Raw;
 using Avalonia.Input.TextInput;
 using Avalonia.Interactivity;
-using Avalonia.VisualTree;
 
 namespace Avalonia.Input
 {
@@ -37,18 +36,21 @@ namespace Avalonia.Input
                     ie.IsKeyboardFocusWithin = false;
                 }
 
-                el = (IInputElement?)el.VisualParent;
+                el = (IInputElement?)(el as Visual)?.VisualParent;
             }
         }
         
         private void ClearFocusWithin(IInputElement element, bool clearRoot)
         {
-            foreach (var visual in element.VisualChildren)
+            if (element is Visual v)
             {
-                if (visual is IInputElement el && el.IsKeyboardFocusWithin)
+                foreach (var visual in v.VisualChildren)
                 {
-                    ClearFocusWithin(el, true);
-                    break;
+                    if (visual is IInputElement el && el.IsKeyboardFocusWithin)
+                    {
+                        ClearFocusWithin(el, true);
+                        break;
+                    }
                 }
             }
             
@@ -81,7 +83,7 @@ namespace Avalonia.Input
                     break;
                 }
 
-                el = el.VisualParent as IInputElement;
+                el = (el as Visual)?.VisualParent as IInputElement;
             }
 
             el = oldElement;
@@ -100,18 +102,21 @@ namespace Avalonia.Input
                     ie.IsKeyboardFocusWithin = true;
                 }
 
-                el = el.VisualParent as IInputElement;
+                el = (el as Visual)?.VisualParent as IInputElement;
             }
         }
         
         private void ClearChildrenFocusWithin(IInputElement element, bool clearRoot)
         {
-            foreach (var visual in element.VisualChildren)
+            if (element is Visual v)
             {
-                if (visual is IInputElement el && el.IsKeyboardFocusWithin)
+                foreach (var visual in v.VisualChildren)
                 {
-                    ClearChildrenFocusWithin(el, true);
-                    break;
+                    if (visual is IInputElement el && el.IsKeyboardFocusWithin)
+                    {
+                        ClearChildrenFocusWithin(el, true);
+                        break;
+                    }
                 }
             }
             
@@ -128,11 +133,11 @@ namespace Avalonia.Input
         {
             if (element != FocusedElement)
             {
-                var interactive = FocusedElement as IInteractive;
+                var interactive = FocusedElement as Interactive;
 
                 if (FocusedElement != null && 
-                    (!FocusedElement.IsAttachedToVisualTree ||
-                     _focusedRoot != element?.VisualRoot as IInputRoot) &&
+                    (!((Visual)FocusedElement).IsAttachedToVisualTree ||
+                     _focusedRoot != ((Visual?)element)?.VisualRoot as IInputRoot) &&
                     _focusedRoot != null)
                 {
                     ClearChildrenFocusWithin(_focusedRoot, true);
@@ -140,14 +145,14 @@ namespace Avalonia.Input
                 
                 SetIsFocusWithin(FocusedElement, element);
                 _focusedElement = element;
-                _focusedRoot = _focusedElement?.VisualRoot as IInputRoot;
+                _focusedRoot = ((Visual?)_focusedElement)?.VisualRoot as IInputRoot;
 
                 interactive?.RaiseEvent(new RoutedEventArgs
                 {
                     RoutedEvent = InputElement.LostFocusEvent,
                 });
 
-                interactive = element as IInteractive;
+                interactive = element as Interactive;
 
                 interactive?.RaiseEvent(new GotFocusEventArgs
                 {
@@ -191,8 +196,8 @@ namespace Avalonia.Input
                             KeyModifiers = keyInput.Modifiers.ToKeyModifiers(),
                             Source = element,
                         };
-
-                        IVisual? currentHandler = element;
+                        
+                        var currentHandler = element as Visual;
                         while (currentHandler != null && !ev.Handled && keyInput.Type == RawKeyEventType.KeyDown)
                         {
                             var bindings = (currentHandler as IInputElement)?.KeyBindings;

+ 1 - 1
src/Avalonia.Base/Input/KeyboardNavigation.cs

@@ -68,7 +68,7 @@ namespace Avalonia.Input
         /// <param name="value">The tab index.</param>
         public static void SetTabIndex(IInputElement element, int value)
         {
-            ((IAvaloniaObject)element).SetValue(TabIndexProperty, value);
+            ((AvaloniaObject)element).SetValue(TabIndexProperty, value);
         }
 
         /// <summary>

+ 3 - 3
src/Avalonia.Base/Input/KeyboardNavigationHandler.cs

@@ -50,7 +50,7 @@ namespace Avalonia.Input
             element = element ?? throw new ArgumentNullException(nameof(element));
 
             // If there's a custom keyboard navigation handler as an ancestor, use that.
-            var custom = element.FindAncestorOfType<ICustomKeyboardNavigation>(true);
+            var custom = (element as Visual)?.FindAncestorOfType<ICustomKeyboardNavigation>(true);
             if (custom is object && HandlePreCustomNavigation(custom, element, direction, out var ce))
                 return ce;
 
@@ -156,9 +156,9 @@ namespace Avalonia.Input
             NavigationDirection direction,
             [NotNullWhen(true)] out IInputElement? result)
         {
-            if (newElement is object)
+            if (newElement is Visual v)
             {
-                var customHandler = newElement.FindAncestorOfType<ICustomKeyboardNavigation>(true);
+                var customHandler = v.FindAncestorOfType<ICustomKeyboardNavigation>(true);
 
                 if (customHandler is object)
                 {

Some files were not shown because too many files changed in this diff