Browse Source

Merge branch 'master' into combobox-scroll-browser

Max Katz 2 years ago
parent
commit
e9d101ede1
100 changed files with 805 additions and 414 deletions
  1. 20 1
      .editorconfig
  2. 5 0
      .ncrunch/AppWithoutLifetime.v3.ncrunchproject
  3. 5 0
      .ncrunch/Avalonia.Headless.NUnit.netstandard2.0.v3.ncrunchproject
  4. 5 0
      .ncrunch/Avalonia.Headless.XUnit.netstandard2.0.v3.ncrunchproject
  5. 0 4
      .nuke/build.schema.json
  6. 7 7
      Avalonia.Desktop.slnf
  7. 42 15
      Avalonia.sln
  8. 1 1
      build/DevAnalyzers.props
  9. 32 0
      build/ExternalConsumers.props
  10. 3 2
      dirs.proj
  11. 3 3
      native/Avalonia.Native/src/OSX/AvnView.mm
  12. 1 1
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  13. 7 6
      nukebuild/Build.cs
  14. 50 43
      nukebuild/RefAssemblyGenerator.cs
  15. 1 1
      nukebuild/_build.csproj
  16. 5 0
      nukebuild/numerge.config
  17. 21 0
      packages/Avalonia/Avalonia.csproj
  18. 2 0
      packages/Avalonia/Avalonia.props
  19. 42 23
      readme.md
  20. 7 0
      samples/AppWithoutLifetime/App.axaml
  21. 12 0
      samples/AppWithoutLifetime/App.axaml.cs
  22. 21 0
      samples/AppWithoutLifetime/AppWithoutLifetime.csproj
  23. 13 0
      samples/AppWithoutLifetime/MainWindow.axaml
  24. 30 0
      samples/AppWithoutLifetime/MainWindow.axaml.cs
  25. 28 0
      samples/AppWithoutLifetime/Program.cs
  26. 9 0
      samples/AppWithoutLifetime/Sub.axaml
  27. 24 0
      samples/AppWithoutLifetime/Sub.axaml.cs
  28. 18 0
      samples/AppWithoutLifetime/app.manifest
  29. 0 2
      samples/ControlCatalog/App.xaml
  30. 0 48
      samples/ControlCatalog/MainView.xaml.cs
  31. 2 1
      samples/ControlCatalog/Pages/ColorPickerPage.xaml
  32. 15 0
      samples/ControlCatalog/Pages/ComboBoxPage.xaml
  33. 5 6
      samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
  34. 1 1
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  35. 2 3
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  36. 25 4
      samples/ControlCatalog/Pages/TabControlPage.xaml
  37. 1 1
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  38. 1 1
      samples/ControlCatalog/Pages/TextBlockPage.xaml
  39. 15 0
      samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs
  40. 1 1
      samples/ControlCatalog/ViewModels/TabControlPageViewModel.cs
  41. 0 2
      samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs
  42. 1 1
      samples/GpuInterop/VulkanDemo/VulkanContext.cs
  43. 1 0
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  44. 3 0
      samples/IntegrationTestApp/MainWindow.axaml
  45. 2 2
      samples/IntegrationTestApp/MainWindow.axaml.cs
  46. 5 5
      samples/RenderDemo/Pages/CustomSkiaPage.cs
  47. 4 4
      samples/RenderDemo/Pages/PathMeasurementPage.cs
  48. 1 1
      samples/RenderDemo/Pages/WriteableBitmapPage.cs
  49. 1 1
      samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj
  50. 3 8
      samples/Sandbox/MainWindow.axaml.cs
  51. 2 0
      samples/Sandbox/Sandbox.csproj
  52. 3 3
      samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs
  53. 58 54
      src/Android/Avalonia.Android/Platform/Input/AndroidKeyboardDevice.cs
  54. 2 2
      src/Avalonia.Base/Animation/Animatable.cs
  55. 7 5
      src/Avalonia.Base/Animation/Animation.cs
  56. 1 1
      src/Avalonia.Base/Animation/AnimatorKeyFrame.cs
  57. 4 2
      src/Avalonia.Base/AttachedProperty.cs
  58. 2 0
      src/Avalonia.Base/Avalonia.Base.csproj
  59. 14 10
      src/Avalonia.Base/AvaloniaProperty.cs
  60. 1 1
      src/Avalonia.Base/AvaloniaPropertyExtensions.cs
  61. 8 6
      src/Avalonia.Base/AvaloniaProperty`1.cs
  62. 2 0
      src/Avalonia.Base/ClassBindingManager.cs
  63. 1 1
      src/Avalonia.Base/Collections/AvaloniaDictionaryExtensions.cs
  64. 1 1
      src/Avalonia.Base/Collections/AvaloniaListConverter.cs
  65. 1 1
      src/Avalonia.Base/Collections/Pooled/ClearMode.cs
  66. 1 1
      src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs
  67. 2 2
      src/Avalonia.Base/Collections/Pooled/PooledList.cs
  68. 2 2
      src/Avalonia.Base/Collections/Pooled/PooledStack.cs
  69. 1 1
      src/Avalonia.Base/Controls/ChildNameScope.cs
  70. 1 1
      src/Avalonia.Base/Controls/IResourceDictionary.cs
  71. 21 0
      src/Avalonia.Base/Controls/IThemeVariantProvider.cs
  72. 7 5
      src/Avalonia.Base/Controls/ResourceDictionary.cs
  73. 46 18
      src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
  74. 2 1
      src/Avalonia.Base/Data/BindingPriority.cs
  75. 2 2
      src/Avalonia.Base/Data/BindingValue.cs
  76. 1 1
      src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
  77. 6 1
      src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs
  78. 1 1
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  79. 1 1
      src/Avalonia.Base/Data/Core/CommonPropertyNames.cs
  80. 1 1
      src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
  81. 1 1
      src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs
  82. 1 1
      src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs
  83. 1 1
      src/Avalonia.Base/Data/IndexerBinding.cs
  84. 2 1
      src/Avalonia.Base/Data/InstancedBinding.cs
  85. 1 1
      src/Avalonia.Base/Data/TemplateBinding.cs
  86. 18 0
      src/Avalonia.Base/Diagnostics/AppliedStyle.cs
  87. 1 1
      src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs
  88. 1 1
      src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs
  89. 2 2
      src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs
  90. 4 4
      src/Avalonia.Base/Diagnostics/TrimmingMessages.cs
  91. 1 40
      src/Avalonia.Base/DirectProperty.cs
  92. 3 3
      src/Avalonia.Base/DirectPropertyBase.cs
  93. 1 1
      src/Avalonia.Base/EnumExtensions.cs
  94. 4 2
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  95. 12 5
      src/Avalonia.Base/Input/Cursor.cs
  96. 2 1
      src/Avalonia.Base/Input/DataFormats.cs
  97. 2 1
      src/Avalonia.Base/Input/DataObjectExtensions.cs
  98. 1 2
      src/Avalonia.Base/Input/DragEventArgs.cs
  99. 41 20
      src/Avalonia.Base/Input/FocusManager.cs
  100. 1 2
      src/Avalonia.Base/Input/IAccessKeyHandler.cs

+ 20 - 1
.editorconfig

@@ -177,7 +177,9 @@ dotnet_diagnostic.CA1828.severity = warning
 dotnet_diagnostic.CA1829.severity = warning
 #CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
 dotnet_diagnostic.CA1847.severity = warning
-#CACA2211:Non-constant fields should not be visible
+#CA1854: Prefer the IDictionary.TryGetValue(TKey, out TValue) method
+dotnet_diagnostic.CA1854.severity = warning
+#CA2211:Non-constant fields should not be visible
 dotnet_diagnostic.CA2211.severity = error
 
 # Wrapping preferences
@@ -186,6 +188,23 @@ csharp_wrap_before_ternary_opsigns = false
 # Avalonia DevAnalyzer preferences
 dotnet_diagnostic.AVADEV2001.severity = error
 
+# Avalonia PublicAnalyzer preferences
+dotnet_diagnostic.AVP1000.severity = error
+dotnet_diagnostic.AVP1001.severity = error
+dotnet_diagnostic.AVP1002.severity = error
+dotnet_diagnostic.AVP1010.severity = error
+dotnet_diagnostic.AVP1011.severity = error
+dotnet_diagnostic.AVP1012.severity = warning
+dotnet_diagnostic.AVP1013.severity = error
+dotnet_diagnostic.AVP1020.severity = error
+dotnet_diagnostic.AVP1021.severity = error
+dotnet_diagnostic.AVP1022.severity = error
+dotnet_diagnostic.AVP1030.severity = error
+dotnet_diagnostic.AVP1031.severity = error
+dotnet_diagnostic.AVP1032.severity = error
+dotnet_diagnostic.AVP1040.severity = error
+dotnet_diagnostic.AVA2001.severity = error
+
 # Xaml files
 [*.{xaml,axaml}]
 indent_size = 2

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

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

+ 5 - 0
.ncrunch/Avalonia.Headless.NUnit.netstandard2.0.v3.ncrunchproject

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

+ 5 - 0
.ncrunch/Avalonia.Headless.XUnit.netstandard2.0.v3.ncrunchproject

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

+ 0 - 4
.nuke/build.schema.json

@@ -101,10 +101,6 @@
           "type": "boolean",
           "description": "skip-tests"
         },
-        "Solution": {
-          "type": "string",
-          "description": "Path to a solution file that is automatically loaded. Default is Avalonia.sln"
-        },
         "Target": {
           "type": "array",
           "description": "List of targets to be invoked. Default is '{default_target}'",

+ 7 - 7
Avalonia.Desktop.slnf

@@ -3,6 +3,7 @@
     "path": "Avalonia.sln",
     "projects": [
       "packages\\Avalonia\\Avalonia.csproj",
+      "samples\\AppWithoutLifetime\\AppWithoutLifetime.csproj",
       "samples\\ControlCatalog.NetCore\\ControlCatalog.NetCore.csproj",
       "samples\\ControlCatalog\\ControlCatalog.csproj",
       "samples\\GpuInterop\\GpuInterop.csproj",
@@ -23,8 +24,6 @@
       "src\\Avalonia.Dialogs\\Avalonia.Dialogs.csproj",
       "src\\Avalonia.Fonts.Inter\\Avalonia.Fonts.Inter.csproj",
       "src\\Avalonia.FreeDesktop\\Avalonia.FreeDesktop.csproj",
-      "src\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj",
-      "src\\Avalonia.Headless\\Avalonia.Headless.csproj",
       "src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj",
       "src\\Avalonia.Native\\Avalonia.Native.csproj",
       "src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj",
@@ -33,19 +32,20 @@
       "src\\Avalonia.Themes.Fluent\\Avalonia.Themes.Fluent.csproj",
       "src\\Avalonia.Themes.Simple\\Avalonia.Themes.Simple.csproj",
       "src\\Avalonia.X11\\Avalonia.X11.csproj",
+      "src\\Headless\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj",
+      "src\\Headless\\Avalonia.Headless\\Avalonia.Headless.csproj",
       "src\\Linux\\Avalonia.LinuxFramebuffer\\Avalonia.LinuxFramebuffer.csproj",
       "src\\Markup\\Avalonia.Markup.Xaml.Loader\\Avalonia.Markup.Xaml.Loader.csproj",
       "src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj",
       "src\\Markup\\Avalonia.Markup\\Avalonia.Markup.csproj",
       "src\\Skia\\Avalonia.Skia\\Avalonia.Skia.csproj",
-      "src\\tools\\Avalonia.Generators\\Avalonia.Generators.csproj",
-      "src\\tools\\Avalonia.Generators\\Avalonia.Generators.csproj",
-      "src\\tools\\DevAnalyzers\\DevAnalyzers.csproj",
-      "src\\tools\\DevGenerators\\DevGenerators.csproj",
-      "src\\tools\\PublicAnalyzers\\Avalonia.Analyzers.csproj",
       "src\\Windows\\Avalonia.Direct2D1\\Avalonia.Direct2D1.csproj",
       "src\\Windows\\Avalonia.Win32.Interop\\Avalonia.Win32.Interop.csproj",
       "src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj",
+      "src\\tools\\Avalonia.Analyzers\\Avalonia.Analyzers.csproj",
+      "src\\tools\\Avalonia.Generators\\Avalonia.Generators.csproj",
+      "src\\tools\\DevAnalyzers\\DevAnalyzers.csproj",
+      "src\\tools\\DevGenerators\\DevGenerators.csproj",
       "tests\\Avalonia.Base.UnitTests\\Avalonia.Base.UnitTests.csproj",
       "tests\\Avalonia.Benchmarks\\Avalonia.Benchmarks.csproj",
       "tests\\Avalonia.Controls.DataGrid.UnitTests\\Avalonia.Controls.DataGrid.UnitTests.csproj",

+ 42 - 15
Avalonia.sln

@@ -233,7 +233,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\R
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\PublicAnalyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}"
 	ProjectSection(SolutionItems) = preProject
@@ -246,12 +246,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepe
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Generators", "src\tools\Avalonia.Generators\Avalonia.Generators.csproj", "{DDA28789-C21A-4654-86CE-D01E81F095C5}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Fonts.Inter", "src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Generators.Sandbox", "samples\Generators.Sandbox\Generators.Sandbox.csproj", "{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppWithoutLifetime", "samples\AppWithoutLifetime\AppWithoutLifetime.csproj", "{F8928267-688E-4A51-989C-612A72446D33}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo", "samples\SafeAreaDemo\SafeAreaDemo.csproj", "{6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.Android", "samples\SafeAreaDemo.Android\SafeAreaDemo.Android.csproj", "{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}"
@@ -262,9 +261,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.iOS", "samples
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Headless", "Headless", "{FF237916-7150-496B-89ED-6CA3292896E7}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit", "src\Headless\Avalonia.Headless.XUnit\Avalonia.Headless.XUnit.csproj", "{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.XUnit", "src\Headless\Avalonia.Headless.XUnit\Avalonia.Headless.XUnit.csproj", "{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.NUnit", "src\Headless\Avalonia.Headless.NUnit\Avalonia.Headless.NUnit.csproj", "{ED976634-B118-43F8-8B26-0279C7A7044F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.UnitTests", "tests\Avalonia.Headless.UnitTests\Avalonia.Headless.UnitTests.csproj", "{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.NUnit.UnitTests", "tests\Avalonia.Headless.NUnit.UnitTests\Avalonia.Headless.NUnit.UnitTests.csproj", "{2999D79E-3C20-4A90-B651-CA7E0AC92D35}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit.UnitTests", "tests\Avalonia.Headless.XUnit.UnitTests\Avalonia.Headless.XUnit.UnitTests.csproj", "{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -597,10 +602,6 @@ Global
 		{DDA28789-C21A-4654-86CE-D01E81F095C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{DDA28789-C21A-4654-86CE-D01E81F095C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{DDA28789-C21A-4654-86CE-D01E81F095C5}.Release|Any CPU.Build.0 = Release|Any CPU
-		{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Release|Any CPU.Build.0 = Release|Any CPU
 		{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -609,14 +610,14 @@ Global
 		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.Build.0 = Release|Any CPU
-		{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2}.Release|Any CPU.Build.0 = Release|Any CPU
 		{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F8928267-688E-4A51-989C-612A72446D33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F8928267-688E-4A51-989C-612A72446D33}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F8928267-688E-4A51-989C-612A72446D33}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F8928267-688E-4A51-989C-612A72446D33}.Release|Any CPU.Build.0 = Release|Any CPU
 		{6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -637,6 +638,26 @@ Global
 		{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Build.0 = Release|Any CPU
 		{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Release|Any CPU.Deploy.0 = Release|Any CPU
+		{ED976634-B118-43F8-8B26-0279C7A7044F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{ED976634-B118-43F8-8B26-0279C7A7044F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{ED976634-B118-43F8-8B26-0279C7A7044F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{ED976634-B118-43F8-8B26-0279C7A7044F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Release|Any CPU.Build.0 = Release|Any CPU
+		{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -681,6 +702,8 @@ Global
 		{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
 		{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+		{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
+		{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
 		{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
 		{11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098}
@@ -707,14 +730,18 @@ Global
 		{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
 		{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
 		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
-		{3B2405E8-9E7A-46D1-8E2D-EF9ED124C9F2} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
-		{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{F8928267-688E-4A51-989C-612A72446D33} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{6B60A970-D5D2-49C2-8BAB-F9C7973B74B6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{4CDAD037-34A2-4CCF-A03A-C6C7B988A572} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{ED976634-B118-43F8-8B26-0279C7A7044F} = {FF237916-7150-496B-89ED-6CA3292896E7}
+		{F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
+		{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+		{2999D79E-3C20-4A90-B651-CA7E0AC92D35} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+		{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

+ 1 - 1
build/DevAnalyzers.props

@@ -5,7 +5,7 @@
                       ReferenceOutputAssembly="false"
                       OutputItemType="Analyzer"
                       SetTargetFramework="TargetFramework=netstandard2.0"/>
-    <ProjectReference Include="$(MSBuildThisFileDirectory)..\src\tools\PublicAnalyzers\Avalonia.Analyzers.csproj"
+    <ProjectReference Include="$(MSBuildThisFileDirectory)..\src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj"
                       PrivateAssets="all"
                       ReferenceOutputAssembly="false"
                       OutputItemType="Analyzer"

+ 32 - 0
build/ExternalConsumers.props

@@ -0,0 +1,32 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <InternalsVisibleTo Include="AvaloniaUI.Xpf.WinApiShim, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a7f4b8b7db0bfb8d74992dc94ecafae031019197ff4263d87ac0a5835fab101c973ccab6fa6e7d90e8f987374f7c6de18dd0b5cd7d6c41e574a8bc66b64836b7c7e707e1aa393d27e33a08f372c1c9965be81658937c85698f4a1c0f73be68a61ffce06d49d1366bf18464c20a29859ccf105fc2d5e35c7ae68919eab668bf8e" />
+    <InternalsVisibleTo Include="System.Windows.Forms, PublicKey=00000000000000000400000000000000" />
+    <InternalsVisibleTo Include="System.Windows.Forms.Primitives, PublicKey=00000000000000000400000000000000" />
+    <InternalsVisibleTo Include="PresentationFramework-SystemData, PublicKey=00000000000000000400000000000000" />
+    <InternalsVisibleTo Include="PresentationFramework.Aero, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="System.Xaml, PublicKey=00000000000000000400000000000000" />
+    <InternalsVisibleTo Include="PresentationFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="PresentationFramework-SystemDrawing, PublicKey=00000000000000000400000000000000" />
+    <InternalsVisibleTo Include="PresentationFramework-SystemCore, PublicKey=00000000000000000400000000000000" />
+    <InternalsVisibleTo Include="WindowsFormsIntegration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="PresentationFramework.AeroLite, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="PresentationFramework.Aero2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="UIAutomationClient, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="PresentationFramework.Luna, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="UIAutomationTypes, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="PresentationFramework.Royale, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="System.Windows.Input.Manipulations, PublicKey=00000000000000000400000000000000" />
+    <InternalsVisibleTo Include="System.Windows.Controls.Ribbon, PublicKey=00000000000000000400000000000000" />
+    <InternalsVisibleTo Include="PresentationFramework-SystemXml, PublicKey=00000000000000000400000000000000" />
+    <InternalsVisibleTo Include="ReachFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="System.Printing, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="PresentationFramework-SystemXmlLinq, PublicKey=00000000000000000400000000000000" />
+    <InternalsVisibleTo Include="PresentationUI, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="Atlantis, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a7f4b8b7db0bfb8d74992dc94ecafae031019197ff4263d87ac0a5835fab101c973ccab6fa6e7d90e8f987374f7c6de18dd0b5cd7d6c41e574a8bc66b64836b7c7e707e1aa393d27e33a08f372c1c9965be81658937c85698f4a1c0f73be68a61ffce06d49d1366bf18464c20a29859ccf105fc2d5e35c7ae68919eab668bf8e" />
+    <InternalsVisibleTo Include="WindowsBase, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="PresentationFramework.Classic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="UIAutomationProvider, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+    <InternalsVisibleTo Include="PresentationCore, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9" />
+  </ItemGroup>
+</Project>

+ 3 - 2
dirs.proj

@@ -9,10 +9,11 @@
     <ProjectReference Remove="**/*.shproj" />
     <ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
     <ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/**/*.*proj" />
-    <!-- Exclude iOS, Android and Web samples from build -->
+    <!-- Exclude iOS, Android and Browser samples from build -->
     <ProjectReference Remove="samples/*.iOS/*.csproj" />
     <ProjectReference Remove="samples/*.Android/*.csproj" />
-    <ProjectReference Remove="samples/*.Web/*.csproj" />
+    <ProjectReference Remove="samples/*.Browser/*.csproj" />
+    <ProjectReference Remove="samples/*.Blazor/*.csproj" />
   </ItemGroup>
   <ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows')) OR '$(MSBuildRuntimeType)' != 'Full'">
     <ProjectReference Remove="src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj" />

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

@@ -275,7 +275,7 @@
         delta.Y = [event deltaY];
     }
 
-    uint32 timestamp = static_cast<uint32>([event timestamp] * 1000);
+    uint64_t timestamp = static_cast<uint64_t>([event timestamp] * 1000);
     auto modifiers = [self getModifiers:[event modifierFlags]];
 
     if(type != Move ||
@@ -444,7 +444,7 @@
 
     auto key = s_KeyMap[[event keyCode]];
 
-    uint32_t timestamp = static_cast<uint32_t>([event timestamp] * 1000);
+    uint64_t timestamp = static_cast<uint64_t>([event timestamp] * 1000);
     auto modifiers = [self getModifiers:[event modifierFlags]];
 
     if(_parent != nullptr)
@@ -657,7 +657,7 @@
     
     [self unmarkText];
         
-    uint32_t timestamp = static_cast<uint32_t>([NSDate timeIntervalSinceReferenceDate] * 1000);
+    uint64_t timestamp = static_cast<uint64_t>([NSDate timeIntervalSinceReferenceDate] * 1000);
         
     _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(timestamp, [text UTF8String]);
     

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

@@ -460,7 +460,7 @@
                     auto point = [self translateLocalPoint:avnPoint];
                     AvnVector delta = { 0, 0 };
 
-                    _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast<uint32>([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
+                    _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast<uint64>([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
                 }
                 
                 if(!_isTransitioningToFullScreen)

+ 7 - 6
nukebuild/Build.cs

@@ -35,8 +35,6 @@ using MicroCom.CodeGenerator;
 
 partial class Build : NukeBuild
 {
-    [Solution("Avalonia.sln")] readonly Solution Solution;
-
     BuildParameters Parameters { get; set; }
     protected override void OnBuildInitialized()
     {
@@ -143,10 +141,12 @@ partial class Build : NukeBuild
     void RunCoreTest(string projectName)
     {
         Information($"Running tests from {projectName}");
-        var project = Solution.GetProject(projectName).NotNull("project != null");
+        var project = RootDirectory.GlobFiles(@$"**\{projectName}.csproj").FirstOrDefault()
+            ?? throw new InvalidOperationException($"Project {projectName} doesn't exist");
+
         // Nuke and MSBuild tools have build-in helpers to get target frameworks from the project.
         // Unfortunately, it gets broken with every second SDK update, so we had to do it manually.
-        var fileXml = XDocument.Parse(File.ReadAllText(project.Path));
+        var fileXml = XDocument.Parse(File.ReadAllText(project));
         var targetFrameworks = fileXml.Descendants("TargetFrameworks")
             .FirstOrDefault()?.Value.Split(';').Select(f => f.Trim());
         if (targetFrameworks is null)
@@ -212,7 +212,8 @@ partial class Build : NukeBuild
             RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
             RunCoreTest("Avalonia.Skia.UnitTests");
             RunCoreTest("Avalonia.ReactiveUI.UnitTests");
-            RunCoreTest("Avalonia.Headless.UnitTests");
+            RunCoreTest("Avalonia.Headless.NUnit.UnitTests");
+            RunCoreTest("Avalonia.Headless.XUnit.UnitTests");
         });
 
     Target RunRenderTests => _ => _
@@ -311,7 +312,7 @@ partial class Build : NukeBuild
 
     public static int Main() =>
         RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
-            ? Execute<Build>(x => x.Package)
+            ? Execute<Build>(x => x.RunToolsTests)
             : Execute<Build>(x => x.RunTests);
 
 }

+ 50 - 43
nukebuild/RefAssemblyGenerator.cs

@@ -63,68 +63,75 @@ public class RefAssemblyGenerator
         });
     }
 
+    static bool HasPrivateApi(IEnumerable<CustomAttribute> attrs) => attrs.Any(a =>
+        a.AttributeType.FullName == "Avalonia.Metadata.PrivateApiAttribute");
+    
     static void ProcessType(TypeDefinition type, MethodReference obsoleteCtor)
     {
         foreach (var nested in type.NestedTypes)
             ProcessType(nested, obsoleteCtor);
-        if (type.IsInterface)
+
+        var hideMethods = (type.IsInterface && type.Name.EndsWith("Impl"))
+                          || HasPrivateApi(type.CustomAttributes);
+
+        var injectMethod = hideMethods
+                           || type.CustomAttributes.Any(a =>
+                               a.AttributeType.FullName == "Avalonia.Metadata.NotClientImplementableAttribute");
+
+
+        
+        if (injectMethod)
         {
-            var hideMethods = type.Name.EndsWith("Impl")
-                              || (type.HasCustomAttributes && type.CustomAttributes.Any(a =>
-                                  a.AttributeType.FullName == "Avalonia.Metadata.PrivateApiAttribute"));
+            type.Methods.Add(new MethodDefinition(
+                "(This interface or abstract class is -not- implementable by user code !)",
+                MethodAttributes.Assembly
+                | MethodAttributes.Abstract
+                | MethodAttributes.NewSlot
+                | MethodAttributes.HideBySig, type.Module.TypeSystem.Void));
+        }
 
-            var injectMethod = hideMethods
-                               || type.CustomAttributes.Any(a =>
-                                   a.AttributeType.FullName == "Avalonia.Metadata.NotClientImplementableAttribute");
-            
-            if (hideMethods)
-            {
-                foreach (var m in type.Methods)
-                {
-                    var dflags = MethodAttributes.Public | MethodAttributes.Family | MethodAttributes.FamORAssem |
-                                 MethodAttributes.FamANDAssem | MethodAttributes.Assembly;
-                    m.Attributes = ((m.Attributes | dflags) ^ dflags) | MethodAttributes.Assembly;
-                }
-            }
-            
-            if(injectMethod)
+        var forceUnstable = type.CustomAttributes.FirstOrDefault(a =>
+            a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute");
+
+        foreach (var m in type.Methods)
+        {
+            if (hideMethods || HasPrivateApi(m.CustomAttributes))
             {
-                type.Methods.Add(new MethodDefinition("NotClientImplementable",
-                    MethodAttributes.Assembly
-                    | MethodAttributes.Abstract
-                    | MethodAttributes.NewSlot
-                    | MethodAttributes.HideBySig, type.Module.TypeSystem.Void));
+                var dflags = MethodAttributes.Public | MethodAttributes.Family | MethodAttributes.FamORAssem |
+                             MethodAttributes.FamANDAssem | MethodAttributes.Assembly;
+                m.Attributes = ((m.Attributes | dflags) ^ dflags) | MethodAttributes.Assembly;
             }
-
-            var forceUnstable = type.CustomAttributes.Any(a =>
-                a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute");
-            
-            foreach (var m in type.Methods)
-                MarkAsUnstable(m, obsoleteCtor, forceUnstable);
-            foreach (var m in type.Properties)
-                MarkAsUnstable(m, obsoleteCtor, forceUnstable);
-            foreach (var m in type.Events)
-                MarkAsUnstable(m, obsoleteCtor, forceUnstable);
-            
+            MarkAsUnstable(m, obsoleteCtor, forceUnstable);
         }
+
+        foreach (var m in type.Properties)
+            MarkAsUnstable(m, obsoleteCtor, forceUnstable);
+        foreach (var m in type.Events)
+            MarkAsUnstable(m, obsoleteCtor, forceUnstable);
     }
 
-    static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, bool force)
+    static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, ICustomAttribute unstableAttribute)
     {
-        if (!force && (
-            def.HasCustomAttributes == false
-            || def.CustomAttributes.All(a => a.AttributeType.FullName != "Avalonia.Metadata.UnstableAttribute")))
+        if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute"))
             return;
 
-        if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute"))
+        unstableAttribute = def.CustomAttributes.FirstOrDefault(a =>
+            a.AttributeType.FullName == "Avalonia.Metadata.UnstableAttribute") ?? unstableAttribute;
+
+        if (unstableAttribute is null)
             return;
 
+        var message = unstableAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString();
+        if (string.IsNullOrEmpty(message))
+        {
+            message = "This is a part of unstable API and can be changed in minor releases. Consider replacing it with alternatives or reach out developers on GitHub.";
+        }
+        
         def.CustomAttributes.Add(new CustomAttribute(obsoleteCtor)
         {
             ConstructorArguments =
             {
-                new CustomAttributeArgument(obsoleteCtor.Module.TypeSystem.String,
-                    "This is a part of unstable API and can be changed in minor releases. You have been warned")
+                new CustomAttributeArgument(obsoleteCtor.Module.TypeSystem.String, message)
             }
         });
     }
@@ -168,4 +175,4 @@ public class RefAssemblyGenerator
                 }
         }
     }
-}
+}

+ 1 - 1
nukebuild/_build.csproj

@@ -15,7 +15,7 @@
     <PackageReference Include="vswhere" Version="2.6.7" Condition=" '$(OS)' == 'Windows_NT' " />
     <PackageReference Include="MicroCom.CodeGenerator" Version="0.11.0" />
     <!-- Keep in sync with Avalonia.Build.Tasks -->
-    <PackageReference Include="Mono.Cecil" Version="0.11.4" />
+    <PackageReference Include="Mono.Cecil" Version="0.11.5" />
     <PackageReference Include="SourceLink" Version="1.1.0" GeneratePathProperty="true" />
     <PackageReference Include="Microsoft.Build.Framework" Version="17.3.2" PrivateAssets="All" />
     <PackageReference Include="xunit.runner.console" Version="2.4.2">

+ 5 - 0
nukebuild/numerge.config

@@ -16,6 +16,11 @@
           "Id": "Avalonia.Generators",
           "IgnoreMissingFrameworkBinaries": true,
           "DoNotMergeDependencies": true
+        },
+        {
+          "Id": "Avalonia.Analyzers",
+          "IgnoreMissingFrameworkBinaries": true,
+          "DoNotMergeDependencies": true
         }
       ]
     }

+ 21 - 0
packages/Avalonia/Avalonia.csproj

@@ -5,6 +5,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+      <PackageReference Include="Avalonia.BuildServices" Version="0.0.16" />
       <ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" />
       <ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj">
         <PrivateAssets>all</PrivateAssets>
@@ -15,6 +16,10 @@
                         ReferenceOutputAssembly="false"
                         PrivateAssets="all"
                         OutputItemType="Analyzer" />
+      <ProjectReference Include="..\..\src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj"
+                        ReferenceOutputAssembly="false"
+                        PrivateAssets="all"
+                        OutputItemType="Analyzer" />
   </ItemGroup>
 
   <PropertyGroup>
@@ -60,4 +65,20 @@
   <Import Project="..\..\build\NetFX.props" />
   <Import Project="..\..\build\CoreLibraries.props" />
   <Import Project="..\..\build\SourceLink.props" Condition="'$(DisableSourceLink)' == ''" />
+
+  <Target Name="WriteCurrentPackageVersionProps" BeforeTargets="_GetPackageFiles">
+    <PropertyGroup>
+      <PackageVersionPropsPath>$(IntermediateOutputPath)/AvaloniaVersion.props</PackageVersionPropsPath>
+    </PropertyGroup>
+    <WriteLinesToFile
+      File="$(PackageVersionPropsPath)"
+      Overwrite="true"
+      Lines="&lt;Project&gt;&lt;PropertyGroup&gt;&lt;AvaloniaMainPackageVersion&gt;$(PackageVersion)&lt;/AvaloniaMainPackageVersion&gt;&lt;/PropertyGroup&gt;&lt;/Project&gt;" />
+    <ItemGroup>
+      <Content Include="$(PackageVersionPropsPath)">
+        <Pack>true</Pack>
+        <PackagePath>build</PackagePath>
+      </Content>
+    </ItemGroup>
+  </Target>
 </Project>

+ 2 - 0
packages/Avalonia/Avalonia.props

@@ -4,9 +4,11 @@
     <AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath>
     <AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
     <AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild>
+    <UsedAvaloniaProducts>$(UsedAvaloniaProducts);AvaloniaUI</UsedAvaloniaProducts>
   </PropertyGroup>
   <Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/>
   <Import Project="$(MSBuildThisFileDirectory)\Avalonia.Generators.props"/>
+  <Import Project="$(MSBuildThisFileDirectory)..\build\AvaloniaVersion.props" />
 
   <!-- Allow loading the AvaloniaVS extension when referencing the Avalonia nuget package -->
   <ItemGroup>

+ 42 - 23
readme.md

@@ -1,26 +1,43 @@
-[![GH_Banner](https://user-images.githubusercontent.com/552074/218457976-92e76834-9e22-4e35-acfa-aa50281bc0f9.png)](https://avaloniaui.net/xpf)
+![Star our repo to show support](https://user-images.githubusercontent.com/552074/235945895-1b896994-a0b6-4e7c-a522-c5688c4ec1b9.png)
+![Header](https://user-images.githubusercontent.com/552074/235865745-2a8e7274-4f66-4f77-8f05-feeb76e7d478.png)
 
 [![Telegram](https://raw.githubusercontent.com/Patrolavia/telegram-badge/master/chat.svg)](https://t.me/Avalonia)
 [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) [![Discord](https://img.shields.io/badge/discord-join%20chat-46BC99)]( https://aka.ms/dotnet-discord) [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) ![License](https://img.shields.io/github/license/avaloniaui/avalonia.svg)
 <br />
 [![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia)  ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) 
 
-# ⚠️ **v11 Update - Pausing community contributions**
-
-for more information see [this](https://github.com/AvaloniaUI/Avalonia/discussions/10599) discussion.
+⚠️ **v11 Update - [Pausing community contributions](https://github.com/AvaloniaUI/Avalonia/discussions/10599)**
 
 ## 📖 About 
 
-Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM.
+[Avalonia](https://avaloniaui.net) is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of platforms such as Windows, macOS, Linux, iOS, Android and WebAssembly. Avalonia is mature and production ready and is used by companies, including [Schneider Electric](https://avaloniaui.net/showcase#se), [Unity](https://avaloniaui.net/showcase#unity), [JetBrains](https://avaloniaui.net/showcase#rider) and [Github](https://avaloniaui.net/showcase#github).
+
+Considered by many to be the spiritual successor to WPF, Avalonia UI provides a familiar, modern development experience for XAML developers creating cross-platform applications. While Avalonia UI is [similar to WPF](https://docs.avaloniaui.net/misc/wpf), it isn't a 1:1 copy, and you'll find plenty of improvements. 
 
-![image](https://user-images.githubusercontent.com/4672627/152126443-932966cf-57e7-4e77-9be6-62463a66b9f8.png)
+For those seeking a cross-platform WPF, we have created [Avalonia XPF](https://avaloniaui.net/xpf), enabling WPF applications to run on macOS and Linux with little to no code changes. Avalonia XPF is a commercial product and is licensed per-app, per-platform. 
 
-To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
+#### Roadmap
+To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). 
+
+#### Breaking Changes
+You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. 
+
+#### Awesome Avalonia
+[Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia!
 
 ## 🚀 Getting Started
 
+See our [Get Started](https://avaloniaui.net/GettingStarted) guide to begin developing apps with Avalonia UI. 
+
+### Visual Studio 
 The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starter guide see our [documentation](https://docs.avaloniaui.net/docs/getting-started).
 
+### JetBrains Rider
+[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
+
+Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
+
+### Avalonia Packages
 Avalonia is delivered via <b>NuGet</b> package manager. You can find the packages here: https://www.nuget.org/packages/Avalonia/
 
 Use these commands in the Package Manager console to install Avalonia manually:
@@ -30,31 +47,26 @@ Install-Package Avalonia.Desktop
 ```
 
 ## Showcase
+[![Showcase_Banner](https://user-images.githubusercontent.com/552074/235946124-bf6fda52-0c9f-4730-868b-0de957e5b97b.png)](https://avaloniaui.net/showcase)
 
-Examples of UIs built with Avalonia
-<video src="https://user-images.githubusercontent.com/4672627/152325602-28df36ec-6444-44a6-aebe-90ad52c8f27a.mp4"></video>
-([Lunacy](https://icons8.com/lunacy))
-
-![image](https://user-images.githubusercontent.com/4672627/152325740-261c27a3-e6f0-4662-bff7-4796d4940e04.png)
-([PlasticSCM](https://www.plasticscm.com/))
 
-![image](https://user-images.githubusercontent.com/4672627/152326453-14944c4d-33da-4d50-a268-b87f80927adb.png)
-([WasabiWallet](https://www.wasabiwallet.io/))
 
-
-## JetBrains Rider
-
-[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia.
-
-Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin.
+See what others have built with Avalonia UI on our [Showcase](https://avaloniaui.net/Showcase). We welcome submissions! 
 
 ## Bleeding Edge Builds
 
 We also have a [nightly build](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed) which tracks the current state of master. Although these packages are less stable than the release on NuGet.org, you'll get all the latest features and bugfixes right away and many of our users actually prefer this feed!
 
-## Documentation
+## Learning
+
+### Documentation 
+Documentation can be found at https://docs.avaloniaui.net. 
 
-Documentation can be found at https://docs.avaloniaui.net. We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers.
+### Tutorials 
+We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers.
+
+### Samples
+We have a [range of samples](https://github.com/AvaloniaUI/Avalonia.Samples) to help you get started.
 
 ## Building and Using
 
@@ -108,6 +120,8 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
 <a href="https://opencollective.com/Avalonia/sponsor/9/website" target="_blank"><img src="https://opencollective.com/Avalonia/sponsor/9/avatar.svg"></a>
 <a href="https://baseheadinc.com/" target="_blank"><img height="50" src="https://baseheadinc.com/wp-content/uploads/2020/09/BH-Logo-for-Site-Header-New.png"></a>
 
+<a href="https://aws.amazon.com/developer/language/net/" target="_blank"><img src="https://github.com/AvaloniaUI/Avalonia/assets/552074/7771d8b9-ef84-4503-9889-033a87d2c852"></a>
+
 ## Commercial Support 
 
 We have a range of [support plans available](https://avaloniaui.net/support) for those looking to partner with the creators of Avalonia, enabling access to the best support at every step of the development process.
@@ -116,3 +130,8 @@ We have a range of [support plans available](https://avaloniaui.net/support) for
 ## .NET Foundation
 
 This project is supported by the [.NET Foundation](https://dotnetfoundation.org).
+
+## Avalonia XPF
+Unleash the full potential of your existing WPF apps with our cross-platform UI framework, enabling WPF apps to run on macOS and Linux without requiring expensive and risky rewrites.
+
+[![GH_Banner](https://user-images.githubusercontent.com/552074/218457976-92e76834-9e22-4e35-acfa-aa50281bc0f9.png)](https://avaloniaui.net/xpf)

+ 7 - 0
samples/AppWithoutLifetime/App.axaml

@@ -0,0 +1,7 @@
+<Application xmlns="https://github.com/avaloniaui"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             x:Class="AppWithoutLifetime.App">
+    <Application.Styles>
+        <FluentTheme />
+    </Application.Styles>
+</Application>

+ 12 - 0
samples/AppWithoutLifetime/App.axaml.cs

@@ -0,0 +1,12 @@
+using Avalonia;
+using Avalonia.Markup.Xaml;
+
+namespace AppWithoutLifetime;
+
+public partial class App : Application
+{
+    public override void Initialize()
+    {
+        AvaloniaXamlLoader.Load(this);
+    }
+}

+ 21 - 0
samples/AppWithoutLifetime/AppWithoutLifetime.csproj

@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
+  </ItemGroup>
+  
+  <Import Project="..\..\build\SampleApp.props" />
+  <Import Project="..\..\build\ReferenceCoreLibraries.props" />
+  <Import Project="..\..\build\BuildTargets.targets" />
+
+</Project>

+ 13 - 0
samples/AppWithoutLifetime/MainWindow.axaml

@@ -0,0 +1,13 @@
+<Window 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"
+        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+        x:Class="AppWithoutLifetime.MainWindow"
+        Title="AppWithoutLifetime">
+  <StackPanel>
+    <TextBlock Text="Welcome to Avalonia!"/>
+    <CheckBox Content="Welcome to Avalonia!"/>
+    <Button Click="Open" Content="Open"/>
+  </StackPanel>
+</Window>

+ 30 - 0
samples/AppWithoutLifetime/MainWindow.axaml.cs

@@ -0,0 +1,30 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+
+namespace AppWithoutLifetime;
+
+public partial class MainWindow : Window
+{
+    public MainWindow()
+    {
+       InitializeComponent();
+    }
+
+    private void InitializeComponent()
+    {
+        AvaloniaXamlLoader.Load(this);
+    }
+
+    protected override void OnLoaded()
+    {
+        this.AttachDevTools();
+        base.OnLoaded();
+    }
+
+    public void Open(object sender, RoutedEventArgs e)
+    {
+        new Sub().Show(this);
+    }
+}

+ 28 - 0
samples/AppWithoutLifetime/Program.cs

@@ -0,0 +1,28 @@
+using Avalonia;
+using Avalonia.Controls;
+using System;
+
+namespace AppWithoutLifetime;
+
+class Program
+{
+    // Initialization code. Don't use any Avalonia, third-party APIs or any
+    // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+    // yet and stuff might break.
+    [STAThread]
+    public static void Main(string[] args)
+    {
+        BuildAvaloniaApp().Start(AppMain, args);
+    }
+
+    private static void AppMain(Application app, string[] args)
+    {
+        app.Run(new MainWindow());
+    }
+
+    // Avalonia configuration, don't remove; also used by visual designer.
+    public static AppBuilder BuildAvaloniaApp()
+        => AppBuilder.Configure<App>()
+            .UsePlatformDetect()
+            .LogToTrace();
+}

+ 9 - 0
samples/AppWithoutLifetime/Sub.axaml

@@ -0,0 +1,9 @@
+<Window 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"
+        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+        x:Class="AppWithoutLifetime.Sub"
+        Title="Window1">
+  Welcome to Avalonia Sub!
+</Window>

+ 24 - 0
samples/AppWithoutLifetime/Sub.axaml.cs

@@ -0,0 +1,24 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace AppWithoutLifetime;
+
+public partial class Sub : Window
+{
+    public Sub()
+    {
+        InitializeComponent();
+    }
+
+    private void InitializeComponent()
+    {
+        AvaloniaXamlLoader.Load(this);
+    }
+
+    protected override void OnLoaded()
+    {
+        this.AttachDevTools();
+        base.OnLoaded();
+    }
+}

+ 18 - 0
samples/AppWithoutLifetime/app.manifest

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <!-- This manifest is used on Windows only.
+       Don't remove it as it might cause problems with window transparency and embeded controls.
+       For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
+  <assemblyIdentity version="1.0.0.0" name="AppWithoutLifetime"/>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- A list of the Windows versions that this application has been tested on
+           and is designed to work with. Uncomment the appropriate elements
+           and Windows will automatically select the most compatible environment. -->
+
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+    </application>
+  </compatibility>
+</assembly>

+ 0 - 2
samples/ControlCatalog/App.xaml

@@ -26,8 +26,6 @@
           <Color x:Key="CatalogBaseHighColor">#FFFFFFFF</Color>
         </ResourceDictionary>
       </ResourceDictionary.ThemeDictionaries>
-      <Color x:Key="SystemAccentColor">#FF0078D7</Color>
-      <Color x:Key="SystemAccentColorDark1">#FF005A9E</Color>
 
       <!-- Styles attached dynamically depending on current theme (simple or fluent) -->
       <StyleInclude x:Key="DataGridFluent" Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />

+ 0 - 48
samples/ControlCatalog/MainView.xaml.cs

@@ -19,13 +19,9 @@ namespace ControlCatalog
 {
     public class MainView : UserControl
     {
-        private readonly IPlatformSettings _platformSettings;
-
         public MainView()
         {
             AvaloniaXamlLoader.Load(this);
-            _platformSettings = AvaloniaLocator.Current.GetRequiredService<IPlatformSettings>();
-            PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
             
             var sideBar = this.Get<TabControl>("Sidebar");
 
@@ -141,50 +137,6 @@ namespace ControlCatalog
                     ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true;
                 };
             }
-
-            _platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged;
-            PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
-        }
-
-        protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
-        {
-            base.OnDetachedFromLogicalTree(e);
-            
-            _platformSettings.ColorValuesChanged -= PlatformSettingsOnColorValuesChanged;
-        }
-
-        private void PlatformSettingsOnColorValuesChanged(object? sender, PlatformColorValues e)
-        {
-            Application.Current!.Resources["SystemAccentColor"] = e.AccentColor1;
-            Application.Current.Resources["SystemAccentColorDark1"] = ChangeColorLuminosity(e.AccentColor1, -0.3);
-            Application.Current.Resources["SystemAccentColorDark2"] = ChangeColorLuminosity(e.AccentColor1, -0.5);
-            Application.Current.Resources["SystemAccentColorDark3"] = ChangeColorLuminosity(e.AccentColor1, -0.7);
-            Application.Current.Resources["SystemAccentColorLight1"] = ChangeColorLuminosity(e.AccentColor1, 0.3);
-            Application.Current.Resources["SystemAccentColorLight2"] = ChangeColorLuminosity(e.AccentColor1, 0.5);
-            Application.Current.Resources["SystemAccentColorLight3"] = ChangeColorLuminosity(e.AccentColor1, 0.7);
-
-            static Color ChangeColorLuminosity(Color color, double luminosityFactor)
-            {
-                var red = (double)color.R;
-                var green = (double)color.G;
-                var blue = (double)color.B;
-
-                if (luminosityFactor < 0)
-                {
-                    luminosityFactor = 1 + luminosityFactor;
-                    red *= luminosityFactor;
-                    green *= luminosityFactor;
-                    blue *= luminosityFactor;
-                }
-                else if (luminosityFactor >= 0)
-                {
-                    red = (255 - red) * luminosityFactor + red;
-                    green = (255 - green) * luminosityFactor + green;
-                    blue = (255 - blue) * luminosityFactor + blue;
-                }
-
-                return new Color(color.A, (byte)red, (byte)green, (byte)blue);
-            }
         }
     }
 }

+ 2 - 1
samples/ControlCatalog/Pages/ColorPickerPage.xaml

@@ -103,7 +103,8 @@
                    HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />-->
       <ColorPreviewer Grid.Row="8"
                       IsAccentColorsVisible="False"
-                      HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" />
+                      HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}"
+                      Margin="0,2,0,0" />
     </Grid>
   </Grid>
 </UserControl>

+ 15 - 0
samples/ControlCatalog/Pages/ComboBoxPage.xaml

@@ -98,6 +98,21 @@
                     <ComboBoxItem>Inline Item 3</ComboBoxItem>
                     <ComboBoxItem>Inline Item 4</ComboBoxItem>
                 </ComboBox>
+
+                <ComboBox WrapSelection="{Binding WrapSelection}" ItemsSource="{Binding Values}" DisplayMemberBinding="{Binding Name}">
+                
+                </ComboBox>
+
+                <ComboBox WrapSelection="{Binding WrapSelection}" ItemsSource="{Binding Values}" >
+                    <ComboBox.ItemTemplate>
+                        <DataTemplate>
+                            <StackPanel Orientation="Horizontal">
+                                <TextBlock Text="{Binding Name}"></TextBlock>
+                                <TextBlock Text="{Binding Id}"></TextBlock>
+                            </StackPanel>
+                        </DataTemplate>
+                    </ComboBox.ItemTemplate>
+                </ComboBox>
             </WrapPanel>
 
             <CheckBox IsChecked="{Binding WrapSelection}">WrapSelection</CheckBox>

+ 5 - 6
samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs

@@ -19,18 +19,16 @@ namespace ControlCatalog.Pages
         public static readonly StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Scale), 1.0d);
         public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); }
 
-        public static readonly StyledProperty<double> RotationProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Rotation));
+        public static readonly StyledProperty<double> RotationProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Rotation),
+            coerce: (_, val) => val % (Math.PI * 2));
+
         /// <summary>
         /// Rotation, measured in Radians!
         /// </summary>
         public double Rotation
         {
             get => GetValue(RotationProperty);
-            set
-            {
-                double valueToUse = value % (Math.PI * 2);
-                SetValue(RotationProperty, valueToUse);
-            }
+            set => SetValue(RotationProperty, value);
         }
 
         public static readonly StyledProperty<double> ViewportCenterYProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterY), 0.0d);
@@ -213,5 +211,6 @@ namespace ControlCatalog.Pages
 
             return workingPoint;
         }
+
     }
 }

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

@@ -307,7 +307,7 @@ namespace ControlCatalog.Pages
             Content:
             ";
 
-                        resultText += await ReadTextFromFile(file, 10000);
+                        resultText += await ReadTextFromFile(file, 500);
                     }
 
                     openedFileContent.Text = resultText;

+ 2 - 3
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@@ -1,5 +1,4 @@
 using System;
-using System.IO;
 using System.Linq;
 using System.Reflection;
 using Avalonia.Controls;
@@ -23,7 +22,7 @@ namespace ControlCatalog.Pages
                 $"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
 
             SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"), DragDropEffects.Move);
-            SetupDnd("Files", d => d.Set(DataFormats.Files, new[] { Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName }), DragDropEffects.Copy);
+            SetupDnd("Files", async d => d.Set(DataFormats.Files, new[] { await (VisualRoot as TopLevel)!.StorageProvider.TryGetFileFromPathAsync(Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName) }), DragDropEffects.Copy);
         }
 
         void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)
@@ -99,7 +98,7 @@ namespace ControlCatalog.Pages
                     {
                         if (item is IStorageFile file)
                         {
-                            var content = await DialogsPage.ReadTextFromFile(file, 1000);
+                            var content = await DialogsPage.ReadTextFromFile(file, 500);
                             contentStr += $"File {item.Name}:{Environment.NewLine}{content}{Environment.NewLine}{Environment.NewLine}";
                         }
                         else if (item is IStorageFolder folder)

+ 25 - 4
samples/ControlCatalog/Pages/TabControlPage.xaml

@@ -4,7 +4,27 @@
     xmlns="https://github.com/avaloniaui"
     xmlns:viewModels="using:ControlCatalog.ViewModels"
     x:DataType="viewModels:TabControlPageViewModel">
-    <DockPanel>
+    <DockPanel Classes.WithContentTemplates="{Binding IsChecked, ElementName=UseContentTemplates}">
+        <DockPanel.Styles>
+            <Style Selector="DockPanel.WithContentTemplates">
+                <Style Selector="^ TabItem">
+                    <Setter Property="ContentTemplate">
+                        <DataTemplate x:CompileBindings="False">
+                            <Border BorderBrush="Red" BorderThickness="10">
+                                <ContentPresenter Content="{Binding}"/>
+                            </Border>
+                        </DataTemplate>
+                    </Setter>
+                </Style>
+                <Style Selector="^ TabControl">
+                    <Setter Property="ContentTemplate">
+                        <DataTemplate>
+                            <TextBlock Text="This template should be overriden by each TabItem's template."/>
+                        </DataTemplate>
+                    </Setter>
+                </Style>
+            </Style>
+        </DockPanel.Styles>
         <TextBlock 
             DockPanel.Dock="Top" 
             Classes="h2"
@@ -55,14 +75,14 @@
                     Margin="0 16"
                     DisplayMemberBinding="{Binding Header, x:DataType=viewModels:TabControlPageViewModelItem}"
                     TabStripPlacement="{Binding TabPlacement}">
-                    <TabControl.ContentTemplate>
+                    <TabControl.DataTemplates>
                         <DataTemplate x:DataType="viewModels:TabControlPageViewModelItem">
                             <StackPanel Orientation="Vertical" Spacing="8">
                                 <TextBlock Text="{Binding Text}"/>
                                 <Image Source="{Binding Image}" Width="300"/>
                             </StackPanel>
                         </DataTemplate>
-                    </TabControl.ContentTemplate>
+                    </TabControl.DataTemplates>
                     <TabControl.Styles>
                         <Style Selector="TabItem" x:DataType="viewModels:TabControlPageViewModelItem">
                             <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
@@ -78,12 +98,13 @@
                 HorizontalAlignment="Center"
                 VerticalAlignment="Center">
                 <TextBlock VerticalAlignment="Center">Tab Placement:</TextBlock>
-                <ComboBox SelectedIndex="{Binding TabPlacement, Mode=TwoWay}">
+                <ComboBox SelectedIndex="{Binding TabPlacement, Mode=TwoWay}" Width="100">
                     <ComboBoxItem>Left</ComboBoxItem>
                     <ComboBoxItem>Bottom</ComboBoxItem>
                     <ComboBoxItem>Right</ComboBoxItem>
                     <ComboBoxItem>Top</ComboBoxItem>
                 </ComboBox>
+                <CheckBox Name="UseContentTemplates">Set TabItem.ContentTemplate</CheckBox>
             </StackPanel>
         </Grid>
     </DockPanel>

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

@@ -49,7 +49,7 @@ namespace ControlCatalog.Pages
             AvaloniaXamlLoader.Load(this);
         }
 
-        private static IBitmap LoadBitmap(string uri)
+        private static Bitmap LoadBitmap(string uri)
         {
             var assets = AvaloniaLocator.Current.GetRequiredService<IAssetLoader>();
             return new Bitmap(assets.Open(new Uri(uri)));

+ 1 - 1
samples/ControlCatalog/Pages/TextBlockPage.xaml

@@ -118,7 +118,7 @@
         </StackPanel>
       </Border>
       <Border>
-        <SelectableTextBlock SelectionBrush="LightBlue" Margin="10" TextWrapping="Wrap">
+        <SelectableTextBlock Margin="10" TextWrapping="Wrap">
           This <Span FontWeight="Bold">is</Span> a
           <Span Background="Silver" Foreground="Maroon">TextBlock</Span>
           with <Span TextDecorations="Underline">several</Span>

+ 15 - 0
samples/ControlCatalog/ViewModels/ComboBoxPageViewModel.cs

@@ -16,5 +16,20 @@ namespace ControlCatalog.ViewModels
             get => _wrapSelection;
             set => this.RaiseAndSetIfChanged(ref _wrapSelection, value);
         }
+
+        public ObservableCollection<IdAndName> Values { get; set; } = new ObservableCollection<IdAndName>
+        {
+            new IdAndName(){ Id = "Id 1", Name = "Name 1" },
+            new IdAndName(){ Id = "Id 2", Name = "Name 2" },
+            new IdAndName(){ Id = "Id 3", Name = "Name 3" },
+            new IdAndName(){ Id = "Id 4", Name = "Name 4" },
+            new IdAndName(){ Id = "Id 5", Name = "Name 5" },
+        };
+    }
+
+    public class IdAndName
+    {
+        public string Id { get; set; }
+        public string Name { get; set; }
     }
 }

+ 1 - 1
samples/ControlCatalog/ViewModels/TabControlPageViewModel.cs

@@ -21,6 +21,6 @@ public class TabControlPageViewModelItem
 {
     public string? Header { get; set; }
     public string? Text { get; set; }
-    public IBitmap? Image { get; set; }
+    public Bitmap? Image { get; set; }
     public bool IsEnabled { get; set; } = true;
 }

+ 0 - 2
samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs

@@ -216,7 +216,6 @@ namespace ControlCatalog.ViewModels
             {
                 var animation = new Animation
                 {
-                    FillMode = FillMode.Forward,
                     Children =
                     {
                         new KeyFrame
@@ -247,7 +246,6 @@ namespace ControlCatalog.ViewModels
                 to.IsVisible = true;
                 var animation = new Animation
                 {
-                    FillMode = FillMode.Forward,
                     Children =
                     {
                         new KeyFrame

+ 1 - 1
samples/GpuInterop/VulkanDemo/VulkanContext.cs

@@ -174,7 +174,7 @@ public unsafe class VulkanContext : IDisposable
                 for (uint queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; queueFamilyIndex++)
                 {
                     var family = familyProperties[queueFamilyIndex];
-                    if (!family.QueueFlags.HasAllFlags(QueueFlags.GraphicsBit))
+                    if (!family.QueueFlags.HasFlag(QueueFlags.GraphicsBit))
                         continue;
 
 

+ 1 - 0
samples/IntegrationTestApp/IntegrationTestApp.csproj

@@ -3,6 +3,7 @@
     <OutputType>WinExe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
     <Nullable>enable</Nullable>
+    <NoWarn>$(NoWarn);AVP1012</NoWarn>
   </PropertyGroup>
 
   <PropertyGroup>

+ 3 - 0
samples/IntegrationTestApp/MainWindow.axaml

@@ -47,6 +47,9 @@
           <Button Name="DisabledButton" IsEnabled="False">
             Disabled Button
           </Button>
+          <Button Name="EffectivelyDisabledButton" Command="{ReflectionBinding DoesntExist}">
+            Effectively Disabled Button
+          </Button>
           <Button Name="BasicButton">
             Basic Button
           </Button>

+ 2 - 2
samples/IntegrationTestApp/MainWindow.axaml.cs

@@ -202,7 +202,7 @@ namespace IntegrationTestApp
         {
             var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
 
-            foreach (var window in lifetime.Windows)
+            foreach (var window in lifetime.Windows.ToArray())
             {
                 window.Activate();
             }
@@ -212,7 +212,7 @@ namespace IntegrationTestApp
         {
             var lifetime = (ClassicDesktopStyleApplicationLifetime)Application.Current!.ApplicationLifetime!;
 
-            foreach (var window in lifetime.Windows)
+            foreach (var window in lifetime.Windows.ToArray())
             {
                 window.Show();
                 if (window.WindowState == WindowState.Minimized)

+ 5 - 5
samples/RenderDemo/Pages/CustomSkiaPage.cs

@@ -27,11 +27,11 @@ namespace RenderDemo.Pages
         
         class CustomDrawOp : ICustomDrawOperation
         {
-            private readonly GlyphRun _noSkia;
+            private readonly IImmutableGlyphRunReference _noSkia;
 
             public CustomDrawOp(Rect bounds, GlyphRun noSkia)
             {
-                _noSkia = noSkia;
+                _noSkia = noSkia.TryCreateImmutableGlyphRunReference();
                 Bounds = bounds;
             }
             
@@ -44,11 +44,11 @@ namespace RenderDemo.Pages
             public bool HitTest(Point p) => false;
             public bool Equals(ICustomDrawOperation other) => false;
             static Stopwatch St = Stopwatch.StartNew();
-            public void Render(IDrawingContextImpl context)
+            public void Render(ImmediateDrawingContext context)
             {
-                var leaseFeature = context.GetFeature<ISkiaSharpApiLeaseFeature>();
+                var leaseFeature = context.TryGetFeature<ISkiaSharpApiLeaseFeature>();
                 if (leaseFeature == null)
-                    context.DrawGlyphRun(Brushes.Black, _noSkia.PlatformImpl);
+                    context.DrawGlyphRun(Brushes.Black, _noSkia);
                 else
                 {
                     using var lease = leaseFeature.Lease();

+ 4 - 4
samples/RenderDemo/Pages/PathMeasurementPage.cs

@@ -53,15 +53,15 @@ namespace RenderDemo.Pages
                 bitmapCtx.DrawGeometry(null, strokePen, basePath);
 
 
-                var length = basePath.PlatformImpl.ContourLength;
+                var length = basePath.ContourLength;
 
-                if (basePath.PlatformImpl.TryGetSegment(length * 0.05, length * 0.2, true, out var dst1))
+                if (basePath.TryGetSegment(length * 0.05, length * 0.2, true, out var dst1))
                     bitmapCtx.DrawGeometry(null, strokePen1, dst1);
 
-                if (basePath.PlatformImpl.TryGetSegment(length * 0.2, length * 0.8, true, out var dst2))
+                if (basePath.TryGetSegment(length * 0.2, length * 0.8, true, out var dst2))
                     bitmapCtx.DrawGeometry(null, strokePen2, dst2);
 
-                if (basePath.PlatformImpl.TryGetSegment(length * 0.8, length * 0.95, true, out var dst3))
+                if (basePath.TryGetSegment(length * 0.8, length * 0.95, true, out var dst3))
                     bitmapCtx.DrawGeometry(null, strokePen3, dst3);
                 
                 var pathBounds = basePath.GetRenderBounds(strokePen);

+ 1 - 1
samples/RenderDemo/Pages/WriteableBitmapPage.cs

@@ -59,7 +59,7 @@ namespace RenderDemo.Pages
                                 color = new Color(fillAlpha, r, g, b);
                             }
 
-                            data[y * fb.Size.Width + x] = (int) color.ToUint32();
+                            data[y * fb.Size.Width + x] = (int) color.ToUInt32();
                         }
                     }
 

+ 1 - 1
samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj

@@ -14,7 +14,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
+    <ProjectReference Include="..\..\src\Headless\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
     <ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
     <ProjectReference Include="..\SafeAreaDemo\SafeAreaDemo.csproj" />

+ 3 - 8
samples/Sandbox/MainWindow.axaml.cs

@@ -1,22 +1,17 @@
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.Presenters;
+using Avalonia.Input.TextInput;
 using Avalonia.Markup.Xaml;
 using Avalonia.Win32.WinRT.Composition;
 
 namespace Sandbox
 {
-    public class MainWindow : Window
+    public partial class MainWindow : Window
     {
         public MainWindow()
         {
-            this.InitializeComponent();
-            this.AttachDevTools();
-        }
-
-        private void InitializeComponent()
-        {
-            AvaloniaXamlLoader.Load(this);
+            InitializeComponent();
         }
     }
 }

+ 2 - 0
samples/Sandbox/Sandbox.csproj

@@ -4,6 +4,7 @@
     <OutputType>WinExe</OutputType>
     <TargetFramework>net6.0</TargetFramework>
     <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
+    <IncludeAvaloniaGenerators>true</IncludeAvaloniaGenerators>
   </PropertyGroup>
 
   <ItemGroup>
@@ -17,4 +18,5 @@
   <Import Project="..\..\build\SampleApp.props" />
   <Import Project="..\..\build\ReferenceCoreLibraries.props" />
   <Import Project="..\..\build\BuildTargets.targets" />
+  <Import Project="..\..\build\SourceGenerators.props" />
 </Project>

+ 3 - 3
samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs

@@ -24,19 +24,19 @@ public class PlaygroundPageViewModel : ViewModelBase
 
     public bool Multiple
     {
-        get => _selectionMode.HasAnyFlag(SelectionMode.Multiple);
+        get => _selectionMode.HasFlag(SelectionMode.Multiple);
         set => SetSelectionMode(SelectionMode.Multiple, value);
     }
 
     public bool Toggle
     {
-        get => _selectionMode.HasAnyFlag(SelectionMode.Toggle);
+        get => _selectionMode.HasFlag(SelectionMode.Toggle);
         set => SetSelectionMode(SelectionMode.Toggle, value);
     }
 
     public bool AlwaysSelected
     {
-        get => _selectionMode.HasAnyFlag(SelectionMode.AlwaysSelected);
+        get => _selectionMode.HasFlag(SelectionMode.AlwaysSelected);
         set => SetSelectionMode(SelectionMode.AlwaysSelected, value);
     }
 

+ 58 - 54
src/Android/Avalonia.Android/Platform/Input/AndroidKeyboardDevice.cs

@@ -5,8 +5,9 @@ using Avalonia.Input;
 
 namespace Avalonia.Android.Platform.Input
 {
-    internal class AndroidKeyboardDevice : KeyboardDevice, IKeyboardDevice {
-    private static readonly Dictionary<Keycode, Key> KeyDic = new Dictionary<Keycode, Key>
+    internal class AndroidKeyboardDevice : KeyboardDevice, IKeyboardDevice
+    {
+        private static readonly Dictionary<Keycode, Key> KeyDic = new Dictionary<Keycode, Key>
      {
          //   { Keycode.Cancel?, Key.Cancel },
             { Keycode.Del, Key.Back },
@@ -15,7 +16,7 @@ namespace Avalonia.Android.Platform.Input
             { Keycode.Clear, Key.Clear },
             { Keycode.Enter, Key.Return },
             { Keycode.MediaPause, Key.Pause },
-            //{ Keycode.?, Key.CapsLock }
+            { Keycode.CapsLock, Key.CapsLock },
             //{ Keycode.?, Key.HangulMode }
             //{ Keycode.?, Key.JunjaMode }
             //{ Keycode.?, Key.FinalMode }
@@ -28,8 +29,8 @@ namespace Avalonia.Android.Platform.Input
             { Keycode.Space, Key.Space },
             { Keycode.PageUp, Key.Prior },
             { Keycode.PageDown, Key.PageDown },
-           // { Keycode.end?, Key.End },
-            { Keycode.Home, Key.Home },
+            { Keycode.MoveEnd, Key.End },
+            { Keycode.MoveHome, Key.Home },
             { Keycode.DpadLeft, Key.Left },
             { Keycode.DpadUp, Key.Up },
             { Keycode.DpadRight, Key.Right },
@@ -37,20 +38,20 @@ namespace Avalonia.Android.Platform.Input
            // { Keycode.ButtonSelect?, Key.Select },
            // { Keycode.print?, Key.Print },
             //{ Keycode.execute?, Key.Execute },
-           // { Keycode.snap, Key.Snapshot }
+            //{ Keycode.snap?, Key.Snapshot }
             { Keycode.Insert, Key.Insert },
             { Keycode.ForwardDel, Key.Delete },
-            //{ Keycode.help, Key.Help },
-            //{ Keycode.?, Key.D0 }
-            //{ Keycode.?, Key.D1 }
-            //{ Keycode.?, Key.D2 }
-            //{ Keycode.?, Key.D3 }
-            //{ Keycode.?, Key.D4 }
-            //{ Keycode.?, Key.D5 }
-            //{ Keycode.?, Key.D6 }
-            //{ Keycode.?, Key.D7 }
-            //{ Keycode.?, Key.D8 }
-            //{ Keycode.?, Key.D9 }
+            { Keycode.Help, Key.Help },
+            { Keycode.Num0, Key.D0 },
+            { Keycode.Num1, Key.D1 },
+            { Keycode.Num2, Key.D2 },
+            { Keycode.Num3, Key.D3 },
+            { Keycode.Num4, Key.D4 },
+            { Keycode.Num5, Key.D5 },
+            { Keycode.Num6, Key.D6 },
+            { Keycode.Num7, Key.D7 },
+            { Keycode.Num8, Key.D8 },
+            { Keycode.Num9, Key.D9 },
             { Keycode.A, Key.A },
             { Keycode.B, Key.B },
             { Keycode.C, Key.C },
@@ -106,22 +107,22 @@ namespace Avalonia.Android.Platform.Input
             //{ Keycode.?, Key.LWin }
             //{ Keycode.?, Key.RWin }
             //{ Keycode.?, Key.Apps }
-            //{ Keycode.?, Key.Sleep }
-            //{ Keycode.?, Key.NumPad0 }
-            //{ Keycode.?, Key.NumPad1 }
-            //{ Keycode.?, Key.NumPad2 }
-            //{ Keycode.?, Key.NumPad3 }
-            //{ Keycode.?, Key.NumPad4 }
-            //{ Keycode.?, Key.NumPad5 }
-            //{ Keycode.?, Key.NumPad6 }
-            //{ Keycode.?, Key.NumPad7 }
-            //{ Keycode.?, Key.NumPad8 }
-            //{ Keycode.?, Key.NumPad9 }
+            { Keycode.Sleep, Key.Sleep },
+            { Keycode.Numpad0, Key.NumPad0 },
+            { Keycode.Numpad1, Key.NumPad1 },
+            { Keycode.Numpad2, Key.NumPad2 },
+            { Keycode.Numpad3, Key.NumPad3 },
+            { Keycode.Numpad4, Key.NumPad4 },
+            { Keycode.Numpad5, Key.NumPad5 },
+            { Keycode.Numpad6, Key.NumPad6 },
+            { Keycode.Numpad7, Key.NumPad7 },
+            { Keycode.Numpad8, Key.NumPad8 },
+            { Keycode.Numpad9, Key.NumPad9 },
             { Keycode.NumpadMultiply, Key.Multiply },
             { Keycode.NumpadAdd, Key.Add },
             { Keycode.NumpadComma, Key.Separator },
             { Keycode.NumpadSubtract, Key.Subtract },
-            //{ Keycode.numpaddecimal?, Key.Decimal }
+            { Keycode.NumpadDot, Key.Decimal },
             { Keycode.NumpadDivide, Key.Divide },
             { Keycode.F1, Key.F1 },
             { Keycode.F2, Key.F2 },
@@ -147,14 +148,14 @@ namespace Avalonia.Android.Platform.Input
             //{ Keycode.R2, Key.F22 },
             //{ Keycode.F23, Key.F23 },
             //{ Keycode.R4, Key.F24 },
-           // { Keycode.numpad, Key.NumLock }
+            { Keycode.NumLock, Key.NumLock },
             { Keycode.ScrollLock, Key.Scroll },
             { Keycode.ShiftLeft, Key.LeftShift },
-            //{ Keycode.?, Key.RightShift }
-            //{ Keycode.?, Key.LeftCtrl }
-            //{ Keycode.?, Key.RightCtrl }
-            //{ Keycode.?, Key.LeftAlt }
-            //{ Keycode.?, Key.RightAlt }
+            { Keycode.ShiftRight, Key.RightShift },
+            { Keycode.CtrlLeft, Key.LeftCtrl },
+            { Keycode.CtrlRight, Key.RightCtrl },
+            { Keycode.AltLeft, Key.LeftAlt },
+            { Keycode.AltRight, Key.RightAlt },
             //{ Keycode.?, Key.BrowserBack }
             //{ Keycode.?, Key.BrowserForward }
             //{ Keycode.?, Key.BrowserRefresh }
@@ -163,28 +164,30 @@ namespace Avalonia.Android.Platform.Input
             //{ Keycode.?, Key.BrowserFavorites }
             //{ Keycode.?, Key.BrowserHome }
             //{ Keycode.?, Key.VolumeMute }
-            //{ Keycode.?, Key.VolumeDown }
-            //{ Keycode.?, Key.VolumeUp }
-            //{ Keycode.?, Key.MediaNextTrack }
-            //{ Keycode.?, Key.MediaPreviousTrack }
-            //{ Keycode.?, Key.MediaStop }
-            //{ Keycode.?, Key.MediaPlayPause }
+            { Keycode.VolumeDown, Key.VolumeDown },
+            { Keycode.VolumeUp, Key.VolumeUp },
+            { Keycode.MediaNext, Key.MediaNextTrack },
+            { Keycode.MediaPrevious, Key.MediaPreviousTrack },
+            { Keycode.MediaStop, Key.MediaStop },
+            { Keycode.MediaPlayPause, Key.MediaPlayPause },
             //{ Keycode.?, Key.LaunchMail }
             //{ Keycode.?, Key.SelectMedia }
             //{ Keycode.?, Key.LaunchApplication1 }
             //{ Keycode.?, Key.LaunchApplication2 }
-            //{ Keycode.?, Key.OemSemicolon }
-            //{ Keycode.?, Key.OemPlus }
-            //{ Keycode.?, Key.OemComma }
-            //{ Keycode.?, Key.OemMinus }
-            //{ Keycode.?, Key.OemPeriod }
+            { Keycode.Semicolon, Key.OemSemicolon },
+            { Keycode.Plus, Key.OemPlus },
+            { Keycode.Comma, Key.OemComma },
+            { Keycode.Minus, Key.OemMinus },
+            { Keycode.Period, Key.OemPeriod },
             //{ Keycode.?, Key.Oem2 }
-            //{ Keycode.?, Key.OemTilde }
+            { Keycode.Grave, Key.OemTilde },
             //{ Keycode.?, Key.AbntC1 }
             //{ Keycode.?, Key.AbntC2 }
-            //{ Keycode.?, Key.Oem4 }
             //{ Keycode.?, Key.OemPipe }
-            //{ Keycode.?, Key.OemCloseBrackets }
+            { Keycode.Apostrophe, Key.OemQuotes },
+            { Keycode.Slash, Key.OemQuestion },
+            { Keycode.LeftBracket, Key.OemOpenBrackets },
+            { Keycode.RightBracket, Key.OemCloseBrackets },
             //{ Keycode.?, Key.Oem7 }
             //{ Keycode.?, Key.Oem8 }
             //{ Keycode.?, Key.Oem102 }
@@ -200,17 +203,18 @@ namespace Avalonia.Android.Platform.Input
             //{ Keycode.?, Key.DbeEnterWordRegisterMode }
             //{ Keycode.?, Key.DbeEnterImeConfigureMode }
             //{ Keycode.?, Key.EraseEof }
-            //{ Keycode.?, Key.Play }
+            { Keycode.MediaPlay, Key.Play },
             //{ Keycode.?, Key.Zoom }
             //{ Keycode.?, Key.NoName }
             //{ Keycode.?, Key.DbeEnterDialogConversionMode }
             //{ Keycode.?, Key.OemClear }
             //{ Keycode.?, Key.DeadCharProcessed }
+            { Keycode.Backslash, Key.OemBackslash }
         };
 
-    internal static Key ConvertKey(Keycode key) {
-      Key result;
-      return KeyDic.TryGetValue(key, out result) ? result : Key.None;
+        internal static Key ConvertKey(Keycode key)
+        {
+            return KeyDic.TryGetValue(key, out var result) ? result : Key.None;
+        }
     }
-  }
 }

+ 2 - 2
src/Avalonia.Base/Animation/Animatable.cs

@@ -58,7 +58,7 @@ namespace Avalonia.Animation
         /// This method should not be called from user code, it will be called automatically by the framework
         /// when a control is added to the visual tree.
         /// </remarks>
-        protected void EnableTransitions()
+        internal void EnableTransitions()
         {
             if (!_transitionsEnabled)
             {
@@ -83,7 +83,7 @@ namespace Avalonia.Animation
         /// This method should not be called from user code, it will be called automatically by the framework
         /// when a control is removed from the visual tree.
         /// </remarks>
-        protected void DisableTransitions()
+        internal void DisableTransitions()
         {
             if (_transitionsEnabled)
             {

+ 7 - 5
src/Avalonia.Base/Animation/Animation.cs

@@ -200,7 +200,7 @@ namespace Avalonia.Animation
         /// </summary>
         /// <param name="setter">The animation setter.</param>
         /// <param name="value">The property animator value.</param>
-        public static void SetAnimator(IAnimationSetter setter, 
+        public static void SetAnimator(IAnimationSetter setter,
             [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods)]
             Type value)
         {
@@ -319,7 +319,7 @@ namespace Avalonia.Animation
             if (animators.Count == 1)
             {
                 var subscription = animators[0].Apply(this, control, clock, match, onComplete);
-                
+
                 if (subscription is not null)
                 {
                     subscriptions.Add(subscription);
@@ -348,9 +348,11 @@ namespace Avalonia.Animation
 
                 if (onComplete != null)
                 {
-                    Task.WhenAll(completionTasks!).ContinueWith(
-                        (_, state) => ((Action)state!).Invoke(),
-                        onComplete);
+                    Task.WhenAll(completionTasks!)
+                        .ContinueWith((_, state) => ((Action)state!).Invoke()
+                            , onComplete
+                            , TaskScheduler.FromCurrentSynchronizationContext()
+                            );
                 }
             }
             return new CompositeDisposable(subscriptions);

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

@@ -67,7 +67,7 @@ namespace Avalonia.Animation
             }
         }
 
-        [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)]
+        [RequiresUnreferencedCode(TrimmingMessages.TypeConversionRequiresUnreferencedCodeMessage)]
         public T GetTypedValue<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>()
         {
             var typeConv = TypeDescriptor.GetConverter(typeof(T));

+ 4 - 2
src/Avalonia.Base/AttachedProperty.cs

@@ -13,16 +13,18 @@ namespace Avalonia
         /// </summary>
         /// <param name="name">The name of the property.</param>
         /// <param name="ownerType">The class that is registering the property.</param>
+        /// <param name="hostType">The class that the property being is registered on.</param>
         /// <param name="metadata">The property metadata.</param>
         /// <param name="inherits">Whether the property inherits its value.</param>
         /// <param name="validate">A value validation callback.</param>
-        public AttachedProperty(
+        internal AttachedProperty(
             string name,
             Type ownerType,
+            Type hostType,
             StyledPropertyMetadata<TValue> metadata,
             bool inherits = false,
             Func<TValue, bool>? validate = null)
-            : base(name, ownerType, metadata, inherits, validate)
+            : base(name, ownerType, hostType, metadata, inherits, validate)
         {
             IsAttached = true;
         }

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

@@ -22,6 +22,7 @@
   <Import Project="..\..\build\SourceGenerators.props" />
   <ItemGroup>
     <Compile Include="..\Shared\IsExternalInit.cs" Link="IsExternalInit.cs" />
+    <Compile Include="..\Shared\ModuleInitializer.cs" Link="ModuleInitializer.cs" />
     <Compile Include="..\Shared\StringCompatibilityExtensions.cs" Link="Compatibility\StringCompatibilityExtensions.cs" />
   </ItemGroup>
 
@@ -62,6 +63,7 @@
     <InternalsVisibleTo Include="Avalonia.iOS, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Dialogs, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)" />
+    <InternalsVisibleTo Include="Avalonia.LinuxFramebuffer, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="MiniMvvm, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="ControlCatalog, PublicKey=$(AvaloniaPublicKey)" />
     <InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" />

+ 14 - 10
src/Avalonia.Base/AvaloniaProperty.cs

@@ -39,12 +39,14 @@ namespace Avalonia
         /// <param name="name">The name of the property.</param>
         /// <param name="valueType">The type of the property's value.</param>
         /// <param name="ownerType">The type of the class that registers the property.</param>
+        /// <param name="hostType">The class that the property being is registered on.</param>
         /// <param name="metadata">The property metadata.</param>
         /// <param name="notifying">A <see cref="Notifying"/> callback.</param>
-        protected AvaloniaProperty(
+        private protected AvaloniaProperty(
             string name,
             Type valueType,
             Type ownerType,
+            Type hostType,
             AvaloniaPropertyMetadata metadata,
             Action<AvaloniaObject, bool>? notifying = null)
         {
@@ -63,9 +65,9 @@ namespace Avalonia
             Notifying = notifying;
             Id = s_nextId++;
 
-            _metadata.Add(ownerType, metadata ?? throw new ArgumentNullException(nameof(metadata)));
+            _metadata.Add(hostType, metadata ?? throw new ArgumentNullException(nameof(metadata)));
             _defaultMetadata = metadata.GenerateTypeSafeMetadata();
-            _singleMetadata = new(ownerType, metadata);
+            _singleMetadata = new(hostType, metadata);
         }
 
         /// <summary>
@@ -74,7 +76,7 @@ namespace Avalonia
         /// <param name="source">The direct property to copy.</param>
         /// <param name="ownerType">The new owner type.</param>
         /// <param name="metadata">Optional overridden metadata.</param>
-        protected AvaloniaProperty(
+        private protected AvaloniaProperty(
             AvaloniaProperty source,
             Type ownerType,
             AvaloniaPropertyMetadata? metadata)
@@ -151,7 +153,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<AvaloniaObject, bool>? Notifying { get; }
+        internal Action<AvaloniaObject, bool>? Notifying { get; }
 
         /// <summary>
         /// Gets the integer ID that represents this property.
@@ -255,6 +257,7 @@ namespace Avalonia
             var result = new StyledProperty<TValue>(
                 name,
                 typeof(TOwner),
+                typeof(TOwner),
                 metadata,
                 inherits,
                 validate);
@@ -301,6 +304,7 @@ namespace Avalonia
             var result = new StyledProperty<TValue>(
                 name,
                 typeof(TOwner),
+                typeof(TOwner),
                 metadata,
                 inherits,
                 validate,
@@ -338,7 +342,7 @@ namespace Avalonia
                 defaultBindingMode: defaultBindingMode,
                 coerce: coerce);
 
-            var result = new AttachedProperty<TValue>(name, typeof(TOwner), metadata, inherits, validate);
+            var result = new AttachedProperty<TValue>(name, typeof(TOwner), typeof(THost), metadata, inherits, validate);
             var registry = AvaloniaPropertyRegistry.Instance;
             registry.Register(typeof(TOwner), result);
             registry.RegisterAttached(typeof(THost), result);
@@ -375,7 +379,7 @@ namespace Avalonia
                 defaultBindingMode: defaultBindingMode,
                 coerce: coerce);
 
-            var result = new AttachedProperty<TValue>(name, ownerType, metadata, inherits, validate);
+            var result = new AttachedProperty<TValue>(name, ownerType, typeof(THost), metadata, inherits, validate);
             var registry = AvaloniaPropertyRegistry.Instance;
             registry.Register(ownerType, result);
             registry.RegisterAttached(typeof(THost), result);
@@ -472,7 +476,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="value">The value.</param>
         /// <returns>True if the value is valid, otherwise false.</returns>
-        [RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)]
+        [RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConversionRequiresUnreferencedCodeMessage)]
         public bool IsValidValue(object? value)
         {
             return TypeUtilities.TryConvertImplicit(PropertyType, value, out _);
@@ -554,7 +558,7 @@ namespace Avalonia
         /// </summary>
         /// <param name="type">The type.</param>
         /// <param name="metadata">The metadata.</param>
-        protected void OverrideMetadata(Type type, AvaloniaPropertyMetadata metadata)
+        private protected void OverrideMetadata(Type type, AvaloniaPropertyMetadata metadata)
         {
             _ = type ?? throw new ArgumentNullException(nameof(type));
             _ = metadata ?? throw new ArgumentNullException(nameof(metadata));
@@ -573,7 +577,7 @@ namespace Avalonia
             _singleMetadata = null;
         }
 
-        protected abstract IObservable<AvaloniaPropertyChangedEventArgs> GetChanged();
+        private protected abstract IObservable<AvaloniaPropertyChangedEventArgs> GetChanged();
 
         private AvaloniaPropertyMetadata GetMetadataWithOverrides(Type type)
         {

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

@@ -7,7 +7,7 @@ namespace Avalonia
     /// <summary>
     /// Extensions for <see cref="AvaloniaProperty"/>.
     /// </summary>
-    public static class AvaloniaPropertyExtensions
+    internal static class AvaloniaPropertyExtensions
     {
         /// <summary>
         /// Checks if values of given property can affect rendering (via <see cref="IAffectsRender"/>).

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

@@ -19,14 +19,16 @@ namespace Avalonia
         /// </summary>
         /// <param name="name">The name of the property.</param>
         /// <param name="ownerType">The type of the class that registers the property.</param>
+        /// <param name="hostType">The class that the property being is registered on.</param>
         /// <param name="metadata">The property metadata.</param>
         /// <param name="notifying">A <see cref="AvaloniaProperty.Notifying"/> callback.</param>
-        protected AvaloniaProperty(
+        private protected AvaloniaProperty(
             string name,
             Type ownerType,
+            Type hostType,
             AvaloniaPropertyMetadata metadata,
             Action<AvaloniaObject, bool>? notifying = null)
-            : base(name, typeof(TValue), ownerType, metadata, notifying)
+            : base(name, typeof(TValue), ownerType, hostType, metadata, notifying)
         {
             _changed = new LightweightSubject<AvaloniaPropertyChangedEventArgs<TValue>>();
         }
@@ -37,7 +39,7 @@ namespace Avalonia
         /// <param name="source">The property to copy.</param>
         /// <param name="ownerType">The new owner type.</param>
         /// <param name="metadata">Optional overridden metadata.</param>
-        protected AvaloniaProperty(
+        private protected AvaloniaProperty(
             AvaloniaProperty<TValue> source,
             Type ownerType,
             AvaloniaPropertyMetadata? metadata)
@@ -66,10 +68,10 @@ namespace Avalonia
             _changed.OnNext(e);
         }
 
-        protected override IObservable<AvaloniaPropertyChangedEventArgs> GetChanged() => Changed;
+        private protected override IObservable<AvaloniaPropertyChangedEventArgs> GetChanged() => Changed;
 
-        [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)]
-        protected BindingValue<object?> TryConvert(object? value)
+        [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConversionSupressWarningMessage)]
+        private protected BindingValue<object?> TryConvert(object? value)
         {
             if (value == UnsetValue)
             {

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

@@ -17,6 +17,8 @@ namespace Avalonia
             return target.Bind(prop, source, anchor);
         }
 
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1001:The same AvaloniaProperty should not be registered twice",
+            Justification = "Classes.attr binding feature is implemented using intermediate avalonia properties for each class")]
         private static AvaloniaProperty RegisterClassProxyProperty(string className)
         {
             var prop = AvaloniaProperty.Register<StyledElement, bool>("__AvaloniaReserved::Classes::" + className);

+ 1 - 1
src/Avalonia.Base/Collections/AvaloniaDictionaryExtensions.cs

@@ -35,7 +35,7 @@ namespace Avalonia.Collections
         /// Indicates if a weak subscription should be used to track changes to the collection.
         /// </param>
         /// <returns>A disposable used to terminate the subscription.</returns>
-        internal static IDisposable ForEachItem<TKey, TValue>(
+        public static IDisposable ForEachItem<TKey, TValue>(
             this IAvaloniaReadOnlyDictionary<TKey, TValue> collection,
             Action<TKey, TValue> added,
             Action<TKey, TValue> removed,

+ 1 - 1
src/Avalonia.Base/Collections/AvaloniaListConverter.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Collections
     /// <summary>
     /// Creates an <see cref="AvaloniaList{T}"/> from a string representation.
     /// </summary>
-    [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)]
+    [RequiresUnreferencedCode(TrimmingMessages.TypeConversionRequiresUnreferencedCodeMessage)]
     public class AvaloniaListConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T> : TypeConverter
     {
         public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)

+ 1 - 1
src/Avalonia.Base/Collections/Pooled/ClearMode.cs

@@ -9,7 +9,7 @@ namespace Avalonia.Collections.Pooled
     /// what each option does before using anything other than the default
     /// of Auto.
     /// </summary>
-    public enum ClearMode
+    internal enum ClearMode
     {
         /// <summary>
         /// <para><code>Auto</code> has different behavior depending on the host project's target framework.</para>

+ 1 - 1
src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs

@@ -11,7 +11,7 @@ namespace Avalonia.Collections.Pooled
     /// </summary>
     /// <typeparam name="T">The type of elements in the read-only pooled list.</typeparam>
 
-    public interface IReadOnlyPooledList<T> : IReadOnlyList<T>
+    internal interface IReadOnlyPooledList<T> : IReadOnlyList<T>
     {
 #pragma warning disable CS0419
         /// <summary>

+ 2 - 2
src/Avalonia.Base/Collections/Pooled/PooledList.cs

@@ -29,7 +29,7 @@ namespace Avalonia.Collections.Pooled
     [DebuggerDisplay("Count = {Count}")]
     [DebuggerTypeProxy(typeof(ICollectionDebugView<>))]
     [Serializable]
-    public class PooledList<T> : IList<T>, IReadOnlyPooledList<T>, IList, IDisposable, IDeserializationCallback
+    internal class PooledList<T> : IList<T>, IReadOnlyPooledList<T>, IList, IDisposable, IDeserializationCallback
     {
         // internal constant copied from Array.MaxArrayLength
         private const int MaxArrayLength = 0x7FEFFFFF;
@@ -1423,7 +1423,7 @@ namespace Avalonia.Collections.Pooled
 
         private static bool ShouldClear(ClearMode mode)
         {
-#if NETCOREAPP2_1
+#if NETCOREAPP2_1_OR_GREATER
             return mode == ClearMode.Always
                 || (mode == ClearMode.Auto && RuntimeHelpers.IsReferenceOrContainsReferences<T>());
 #else

+ 2 - 2
src/Avalonia.Base/Collections/Pooled/PooledStack.cs

@@ -29,7 +29,7 @@ namespace Avalonia.Collections.Pooled
     [DebuggerTypeProxy(typeof(StackDebugView<>))]
     [DebuggerDisplay("Count = {Count}")]
     [Serializable]
-    public class PooledStack<T> : IEnumerable<T>, ICollection, IReadOnlyCollection<T>, IDisposable, IDeserializationCallback
+    internal class PooledStack<T> : IEnumerable<T>, ICollection, IReadOnlyCollection<T>, IDisposable, IDeserializationCallback
     {
         [NonSerialized]
         private ArrayPool<T> _pool;
@@ -596,7 +596,7 @@ namespace Avalonia.Collections.Pooled
 
         private static bool ShouldClear(ClearMode mode)
         {
-#if NETCOREAPP2_1
+#if NETCOREAPP2_1_OR_GREATER
             return mode == ClearMode.Always
                 || (mode == ClearMode.Auto && RuntimeHelpers.IsReferenceOrContainsReferences<T>());
 #else

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

@@ -3,7 +3,7 @@ using Avalonia.Utilities;
 
 namespace Avalonia.Controls
 {
-    public class ChildNameScope : INameScope
+    internal class ChildNameScope : INameScope
     {
         private readonly INameScope _parentScope;
         private readonly NameScope _inner = new NameScope();

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

@@ -18,6 +18,6 @@ namespace Avalonia.Controls
         /// <summary>
         /// Gets a collection of merged resource dictionaries that are specifically keyed and composed to address theme scenarios.
         /// </summary>
-        IDictionary<ThemeVariant, IResourceProvider> ThemeDictionaries { get; }
+        IDictionary<ThemeVariant, IThemeVariantProvider> ThemeDictionaries { get; }
     }
 }

+ 21 - 0
src/Avalonia.Base/Controls/IThemeVariantProvider.cs

@@ -0,0 +1,21 @@
+using Avalonia.Metadata;
+using Avalonia.Styling;
+
+namespace Avalonia.Controls;
+
+/// <summary>
+/// Resource provider with theme variant awareness.
+/// Can be used with <see cref="IResourceDictionary.ThemeDictionaries"/>.
+/// </summary>
+/// <remarks>
+/// This is a helper interface for the XAML compiler to make Key property accessibly by the markup extensions.
+/// Which means, it can only be used with ResourceDictionaries and markup extensions in the XAML code.
+/// </remarks>
+[Unstable("This XAML-only API might be removed in the future minor updates.")]
+public interface IThemeVariantProvider : IResourceProvider
+{
+    /// <summary>
+    /// Key property set by the compiler.
+    /// </summary>
+    ThemeVariant? Key { get; set; }
+}

+ 7 - 5
src/Avalonia.Base/Controls/ResourceDictionary.cs

@@ -13,13 +13,13 @@ namespace Avalonia.Controls
     /// <summary>
     /// An indexed dictionary of resources.
     /// </summary>
-    public class ResourceDictionary : IResourceDictionary
+    public class ResourceDictionary : IResourceDictionary, IThemeVariantProvider
     {
         private object? lastDeferredItemKey;
         private Dictionary<object, object?>? _inner;
         private IResourceHost? _owner;
         private AvaloniaList<IResourceProvider>? _mergedDictionaries;
-        private AvaloniaDictionary<ThemeVariant, IResourceProvider>? _themeDictionary;
+        private AvaloniaDictionary<ThemeVariant, IThemeVariantProvider>? _themeDictionary;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ResourceDictionary"/> class.
@@ -93,13 +93,13 @@ namespace Avalonia.Controls
             }
         }
 
-        public IDictionary<ThemeVariant, IResourceProvider> ThemeDictionaries
+        public IDictionary<ThemeVariant, IThemeVariantProvider> ThemeDictionaries
         {
             get
             {
                 if (_themeDictionary == null)
                 {
-                    _themeDictionary = new AvaloniaDictionary<ThemeVariant, IResourceProvider>(2);
+                    _themeDictionary = new AvaloniaDictionary<ThemeVariant, IThemeVariantProvider>(2);
                     _themeDictionary.ForEachItem(
                         (_, x) =>
                         {
@@ -120,6 +120,8 @@ namespace Avalonia.Controls
                 return _themeDictionary;
             }
         }
+        
+        ThemeVariant? IThemeVariantProvider.Key { get; set; }
 
         bool IResourceNode.HasResources
         {
@@ -192,7 +194,7 @@ namespace Avalonia.Controls
 
             if (_themeDictionary is not null)
             {
-                IResourceProvider? themeResourceProvider;
+                IThemeVariantProvider? themeResourceProvider;
                 if (theme is not null && theme != ThemeVariant.Default)
                 {
                     if (_themeDictionary.TryGetValue(theme, out themeResourceProvider)

+ 46 - 18
src/Avalonia.Base/Controls/ResourceNodeExtensions.cs

@@ -119,7 +119,19 @@ namespace Avalonia.Controls
             resourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider));
             key = key ?? throw new ArgumentNullException(nameof(key));
 
-            return new FloatingResourceObservable(resourceProvider, key, converter);
+            return new FloatingResourceObservable(resourceProvider, key, null, converter);
+        }
+
+        public static IObservable<object?> GetResourceObservable(
+            this IResourceProvider resourceProvider,
+            object key,
+            ThemeVariant? defaultThemeVariant,
+            Func<object?, object?>? converter = null)
+        {
+            resourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider));
+            key = key ?? throw new ArgumentNullException(nameof(key));
+
+            return new FloatingResourceObservable(resourceProvider, key, defaultThemeVariant, converter);
         }
 
         private class ResourceObservable : LightweightObservableBase<object?>
@@ -128,7 +140,10 @@ namespace Avalonia.Controls
             private readonly object _key;
             private readonly Func<object?, object?>? _converter;
 
-            public ResourceObservable(IResourceHost target, object key, Func<object?, object?>? converter)
+            public ResourceObservable(
+                IResourceHost target,
+                object key,
+                Func<object?, object?>? converter)
             {
                 _target = target;
                 _key = key;
@@ -170,11 +185,8 @@ namespace Avalonia.Controls
 
             private object? GetValue()
             {
-                if (_target is not IThemeVariantHost themeVariantHost
-                    || !_target.TryFindResource(_key, themeVariantHost.ActualThemeVariant, out var value))
-                {
-                    value = _target.FindResource(_key) ?? AvaloniaProperty.UnsetValue;
-                }
+                var theme = (_target as IThemeVariantHost)?.ActualThemeVariant; 
+                var value = _target.FindResource(theme, _key) ?? AvaloniaProperty.UnsetValue;
 
                 return _converter?.Invoke(value) ?? value;
             }
@@ -183,14 +195,20 @@ namespace Avalonia.Controls
         private class FloatingResourceObservable : LightweightObservableBase<object?>
         {
             private readonly IResourceProvider _target;
+            private readonly ThemeVariant? _overrideThemeVariant;
             private readonly object _key;
             private readonly Func<object?, object?>? _converter;
             private IResourceHost? _owner;
 
-            public FloatingResourceObservable(IResourceProvider target, object key, Func<object?, object?>? converter)
+            public FloatingResourceObservable(
+                IResourceProvider target,
+                object key,
+                ThemeVariant? overrideThemeVariant,
+                Func<object?, object?>? converter)
             {
                 _target = target;
                 _key = key;
+                _overrideThemeVariant = overrideThemeVariant;
                 _converter = converter;
             }
 
@@ -203,11 +221,25 @@ namespace Avalonia.Controls
                 {
                     _owner.ResourcesChanged += ResourcesChanged;
                 }
+                if (_overrideThemeVariant is null && _owner is IThemeVariantHost themeVariantHost)
+                {
+                    themeVariantHost.ActualThemeVariantChanged += ActualThemeVariantChanged;
+                }
             }
 
             protected override void Deinitialize()
             {
                 _target.OwnerChanged -= OwnerChanged;
+
+                if (_owner is not null)
+                {
+                    _owner.ResourcesChanged -= ResourcesChanged;
+                }
+                if (_overrideThemeVariant is null && _owner is IThemeVariantHost themeVariantHost)
+                {
+                    themeVariantHost.ActualThemeVariantChanged -= ActualThemeVariantChanged;
+                }
+
                 _owner = null;
             }
 
@@ -233,9 +265,9 @@ namespace Avalonia.Controls
                 {
                     _owner.ResourcesChanged -= ResourcesChanged;
                 }
-                if (_owner is IThemeVariantHost themeVariantHost)
+                if (_overrideThemeVariant is null && _owner is IThemeVariantHost themeVariantHost)
                 {
-                    themeVariantHost.ActualThemeVariantChanged += ActualThemeVariantChanged;
+                    themeVariantHost.ActualThemeVariantChanged -= ActualThemeVariantChanged;
                 }
 
                 _owner = _target.Owner;
@@ -244,12 +276,11 @@ namespace Avalonia.Controls
                 {
                     _owner.ResourcesChanged += ResourcesChanged;
                 }
-                if (_owner is IThemeVariantHost themeVariantHost2)
+                if (_overrideThemeVariant is null && _owner is IThemeVariantHost themeVariantHost2)
                 {
-                    themeVariantHost2.ActualThemeVariantChanged -= ActualThemeVariantChanged;
+                    themeVariantHost2.ActualThemeVariantChanged += ActualThemeVariantChanged;
                 }
 
-
                 PublishNext();
             }
 
@@ -265,11 +296,8 @@ namespace Avalonia.Controls
 
             private object? GetValue()
             {
-                if (!(_target.Owner is IThemeVariantHost themeVariantHost)
-                    || !_target.Owner.TryFindResource(_key, themeVariantHost.ActualThemeVariant, out var value))
-                {
-                    value = _target.Owner?.FindResource(_key) ?? AvaloniaProperty.UnsetValue;
-                }
+                var theme = _overrideThemeVariant ?? (_target.Owner as IThemeVariantHost)?.ActualThemeVariant; 
+                var value = _target.Owner?.FindResource(theme, _key) ?? AvaloniaProperty.UnsetValue;
 
                 return _converter?.Invoke(value) ?? value;
             }

+ 2 - 1
src/Avalonia.Base/Data/BindingPriority.cs

@@ -1,4 +1,5 @@
 using System;
+using System.ComponentModel;
 
 namespace Avalonia.Data
 {
@@ -47,7 +48,7 @@ namespace Avalonia.Data
         /// </summary>
         Unset = int.MaxValue,
 
-        [Obsolete("Use Template priority")]
+        [Obsolete("Use Template priority"), EditorBrowsable(EditorBrowsableState.Never)]
         TemplatedParent = Template,
     }
 }

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

@@ -237,7 +237,7 @@ namespace Avalonia.Data
         /// </summary>
         /// <param name="value">The untyped value.</param>
         /// <returns>The typed binding value.</returns>
-        [RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)]
+        [RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConversionRequiresUnreferencedCodeMessage)]
         public static BindingValue<T> FromUntyped(object? value)
         {
             return FromUntyped(value, typeof(T));
@@ -251,7 +251,7 @@ namespace Avalonia.Data
         /// <param name="value">The untyped value.</param>
         /// <param name="targetType">The runtime target type.</param>
         /// <returns>The typed binding value.</returns>
-        [RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)]
+        [RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConversionRequiresUnreferencedCodeMessage)]
         public static BindingValue<T> FromUntyped(object? value, Type targetType)
         {
             if (value == AvaloniaProperty.UnsetValue)

+ 1 - 1
src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Data.Converters
     /// Provides a default set of value conversions for bindings that do not specify a value
     /// converter.
     /// </summary>
-    [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)]
+    [RequiresUnreferencedCode(TrimmingMessages.TypeConversionRequiresUnreferencedCodeMessage)]
     public class DefaultValueConverter : IValueConverter
     {
         /// <summary>

+ 6 - 1
src/Avalonia.Base/Data/Converters/StringFormatValueConverter.cs

@@ -35,7 +35,12 @@ namespace Avalonia.Data.Converters
         public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
             value = Inner?.Convert(value, targetType, parameter, culture) ?? value;
-            return string.Format(culture, Format, value);
+            var format = Format!;
+            if (!format.Contains('{'))
+            {
+                format = $"{{0:{format}}}";
+            }
+            return string.Format(culture, format, value);
         }
 
         /// <inheritdoc/>

+ 1 - 1
src/Avalonia.Base/Data/Core/BindingExpression.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Data.Core
     /// Binds to an expression on an object using a type value converter to convert the values
     /// that are sent and received.
     /// </summary>
-    [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)]
+    [RequiresUnreferencedCode(TrimmingMessages.TypeConversionRequiresUnreferencedCodeMessage)]
     internal class BindingExpression : LightweightObservableBase<object?>, IAvaloniaSubject<object?>, IDescription
     {
         private readonly ExpressionObserver _inner;

+ 1 - 1
src/Avalonia.Base/Data/Core/CommonPropertyNames.cs

@@ -1,6 +1,6 @@
 namespace Avalonia.Data.Core
 {
-    public static class CommonPropertyNames
+    internal static class CommonPropertyNames
     {
         public const string IndexerName = "Item";
     }

+ 1 - 1
src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Data.Core.Plugins
     /// <summary>
     /// Validates properties on that have <see cref="ValidationAttribute"/>s.
     /// </summary>
-    internal class DataAnnotationsValidationPlugin : IDataValidationPlugin
+    public class DataAnnotationsValidationPlugin : IDataValidationPlugin
     {
         /// <inheritdoc/>
         [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]

+ 1 - 1
src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs

@@ -7,7 +7,7 @@ namespace Avalonia.Data.Core.Plugins
     /// <summary>
     /// Validates properties that report errors by throwing exceptions.
     /// </summary>
-    internal class ExceptionValidationPlugin : IDataValidationPlugin
+    public class ExceptionValidationPlugin : IDataValidationPlugin
     {
         /// <inheritdoc/>
         [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]

+ 1 - 1
src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Data.Core.Plugins
     /// <summary>
     /// Validates properties on objects that implement <see cref="INotifyDataErrorInfo"/>.
     /// </summary>
-    internal class IndeiValidationPlugin : IDataValidationPlugin
+    public class IndeiValidationPlugin : IDataValidationPlugin
     {
         private static readonly WeakEvent<INotifyDataErrorInfo, DataErrorsChangedEventArgs>
             ErrorsChangedWeakEvent = WeakEvent.Register<INotifyDataErrorInfo, DataErrorsChangedEventArgs>(

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

@@ -2,7 +2,7 @@
 
 namespace Avalonia.Data
 {
-    public class IndexerBinding : IBinding
+    internal class IndexerBinding : IBinding
     {
         public IndexerBinding(
             AvaloniaObject source,

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

@@ -1,4 +1,5 @@
 using System;
+using System.ComponentModel;
 using Avalonia.Reactive;
 using ObservableEx = Avalonia.Reactive.Observable;
 
@@ -49,7 +50,7 @@ namespace Avalonia.Data
         /// </summary>
         public IObservable<object?> Source { get; }
 
-        [Obsolete("Use Source property")]
+        [Obsolete("Use Source property"), EditorBrowsable(EditorBrowsableState.Never)]
         public IObservable<object?> Observable => Source;
 
         /// <summary>

+ 1 - 1
src/Markup/Avalonia.Markup/Data/TemplateBinding.cs → src/Avalonia.Base/Data/TemplateBinding.cs

@@ -109,7 +109,7 @@ namespace Avalonia.Data
         }
 
         /// <inheritdoc/>
-        void ISetterValue.Initialize(ISetter setter) => _isSetterValue = true;
+        void ISetterValue.Initialize(SetterBase setter) => _isSetterValue = true;
 
         protected override void Subscribed()
         {

+ 18 - 0
src/Avalonia.Base/Diagnostics/AppliedStyle.cs

@@ -0,0 +1,18 @@
+using Avalonia.Styling;
+
+namespace Avalonia.Diagnostics
+{
+    public class AppliedStyle
+    {
+        private readonly IStyleInstance _instance;
+
+        internal AppliedStyle(IStyleInstance instance)
+        {
+            _instance = instance;
+        }
+
+        public bool HasActivator => _instance.HasActivator;
+        public bool IsActive => _instance.IsActive;
+        public StyleBase Style => (StyleBase)_instance.Source;
+    }
+}

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

@@ -5,7 +5,7 @@ namespace Avalonia.Diagnostics
     /// <summary>
     /// Provides a debug interface into <see cref="AvaloniaObject"/>.
     /// </summary>
-    public interface IAvaloniaObjectDebug
+    internal interface IAvaloniaObjectDebug
     {
         /// <summary>
         /// Gets the subscriber list for the <see cref="AvaloniaObject.PropertyChanged"/>

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

@@ -8,7 +8,7 @@ namespace Avalonia.Diagnostics
     /// Provides a debug interface into <see cref="INotifyCollectionChanged"/> subscribers on
     /// <see cref="AvaloniaList{T}"/>
     /// </summary>
-    public interface INotifyCollectionChangedDebug
+    internal interface INotifyCollectionChangedDebug
     {
         /// <summary>
         /// Gets the subscriber list for the <see cref="INotifyCollectionChanged.CollectionChanged"/>

+ 2 - 2
src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs

@@ -11,9 +11,9 @@ namespace Avalonia.Diagnostics
         /// <summary>
         /// Currently applied styles.
         /// </summary>
-        public IReadOnlyList<IStyleInstance> AppliedStyles { get; }
+        public IReadOnlyList<AppliedStyle> AppliedStyles { get; }
 
-        public StyleDiagnostics(IReadOnlyList<IStyleInstance> appliedStyles)
+        public StyleDiagnostics(IReadOnlyList<AppliedStyle> appliedStyles)
         {
             AppliedStyles = appliedStyles;
         }

+ 4 - 4
src/Avalonia.Base/Diagnostics/TrimmingMessages.cs

@@ -2,11 +2,11 @@
 
 internal static class TrimmingMessages
 {
-    public const string ImplicitTypeConvertionSupressWarningMessage = "Implicit convertion methods might be removed by the linker. We don't have a reliable way to prevent it, except converting everything in compile time when possible.";
-    public const string ImplicitTypeConvertionRequiresUnreferencedCodeMessage = "Implicit convertion methods are required for type conversion.";
+    public const string ImplicitTypeConversionSupressWarningMessage = "Implicit conversion methods might be removed by the linker. We don't have a reliable way to prevent it, except converting everything in compile time when possible.";
+    public const string ImplicitTypeConversionRequiresUnreferencedCodeMessage = "Implicit conversion methods are required for type conversion.";
 
-    public const string TypeConvertionSupressWarningMessage = "Convertion methods might be removed by the linker. We don't have a reliable way to prevent it, except converting everything in compile time when possible.";
-    public const string TypeConvertionRequiresUnreferencedCodeMessage = "Convertion methods are required for type conversion, including op_Implicit, op_Explicit, Parse and TypeConverter.";
+    public const string TypeConversionSupressWarningMessage = "Conversion methods might be removed by the linker. We don't have a reliable way to prevent it, except converting everything in compile time when possible.";
+    public const string TypeConversionRequiresUnreferencedCodeMessage = "Conversion methods are required for type conversion, including op_Implicit, op_Explicit, Parse and TypeConverter.";
 
     public const string ReflectionBindingRequiresUnreferencedCodeMessage = "BindingExpression and ReflectionBinding heavily use reflection. Consider using CompiledBindings instead.";
     public const string ReflectionBindingSupressWarningMessage = "BindingExpression and ReflectionBinding internal heavily use reflection.";

+ 1 - 40
src/Avalonia.Base/DirectProperty.cs

@@ -24,7 +24,7 @@ namespace Avalonia
         /// <param name="getter">Gets the current value of the property.</param>
         /// <param name="setter">Sets the value of the property. May be null.</param>
         /// <param name="metadata">The property metadata.</param>
-        public DirectProperty(
+        internal DirectProperty(
             string name,
             Func<TOwner, TValue> getter,
             Action<TOwner, TValue>? setter,
@@ -106,45 +106,6 @@ namespace Avalonia
             return result;
         }
 
-        /// <summary>
-        /// Registers the direct property on another type.
-        /// </summary>
-        /// <typeparam name="TNewOwner">The type of the additional owner.</typeparam>
-        /// <param name="getter">Gets the current value of the property.</param>
-        /// <param name="setter">Sets the value of the property.</param>
-        /// <param name="unsetValue">
-        /// The value to use when the property is set to <see cref="AvaloniaProperty.UnsetValue"/>
-        /// </param>
-        /// <param name="defaultBindingMode">The default binding mode for the property.</param>
-        /// <param name="enableDataValidation">
-        /// Whether the property is interested in data validation.
-        /// </param>
-        /// <returns>The property.</returns>
-        public DirectProperty<TNewOwner, TValue> AddOwnerWithDataValidation<TNewOwner>(
-            Func<TNewOwner, TValue> getter,
-            Action<TNewOwner,TValue> setter,
-            TValue unsetValue = default!,
-            BindingMode defaultBindingMode = BindingMode.Default,
-            bool enableDataValidation = false)
-                where TNewOwner : AvaloniaObject
-        {
-            var metadata = new DirectPropertyMetadata<TValue>(
-                unsetValue: unsetValue,
-                defaultBindingMode: defaultBindingMode,
-                enableDataValidation: enableDataValidation);
-
-            metadata.Merge(GetMetadata<TOwner>(), this);
-
-            var result = new DirectProperty<TNewOwner, TValue>(
-                this,
-                getter,
-                setter,
-                metadata);
-
-            AvaloniaPropertyRegistry.Instance.Register(typeof(TNewOwner), result);
-            return result;
-        }
-
         /// <inheritdoc/>
         internal override TValue InvokeGetter(AvaloniaObject instance)
         {

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

@@ -20,11 +20,11 @@ namespace Avalonia
         /// <param name="name">The name of the property.</param>
         /// <param name="ownerType">The type of the class that registers the property.</param>
         /// <param name="metadata">The property metadata.</param>
-        protected DirectPropertyBase(
+        private protected DirectPropertyBase(
             string name,
             Type ownerType,
             AvaloniaPropertyMetadata metadata)
-            : base(name, ownerType, metadata)
+            : base(name, ownerType, ownerType, metadata)
         {
             Owner = ownerType;
         }
@@ -35,7 +35,7 @@ namespace Avalonia
         /// <param name="source">The property to copy.</param>
         /// <param name="ownerType">The new owner type.</param>
         /// <param name="metadata">Optional overridden metadata.</param>
-        protected DirectPropertyBase(
+        private protected DirectPropertyBase(
             DirectPropertyBase<TValue> source,
             Type ownerType,
             AvaloniaPropertyMetadata metadata)

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

@@ -6,7 +6,7 @@ namespace Avalonia
     /// <summary>
     /// Provides extension methods for enums.
     /// </summary>
-    public static class EnumExtensions
+    internal static class EnumExtensions
     {
             
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

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

@@ -9,7 +9,7 @@ namespace Avalonia.Input
     /// <summary>
     /// Handles access keys for a window.
     /// </summary>
-    public class AccessKeyHandler : IAccessKeyHandler
+    internal class AccessKeyHandler : IAccessKeyHandler
     {
         /// <summary>
         /// Defines the AccessKeyPressed attached event.
@@ -141,9 +141,11 @@ namespace Avalonia.Input
 
                 if (MainMenu == null || !MainMenu.IsOpen)
                 {
+                    var focusManager = FocusManager.GetFocusManager(e.Source as IInputElement);
+                    
                     // TODO: Use FocusScopes to store the current element and restore it when context menu is closed.
                     // Save currently focused input element.
-                    _restoreFocusElement = FocusManager.Instance?.Current;
+                    _restoreFocusElement = focusManager?.GetFocusedElement();
 
                     // When Alt is pressed without a main menu, or with a closed main menu, show
                     // access key markers in the window (i.e. "_File").

+ 12 - 5
src/Avalonia.Base/Input/Cursor.cs

@@ -42,23 +42,25 @@ namespace Avalonia.Input
     public class Cursor : IDisposable
     {
         public static readonly Cursor Default = new Cursor(StandardCursorType.Arrow);
+        private string _name;
 
-        internal Cursor(ICursorImpl platformImpl)
+        private Cursor(ICursorImpl platformImpl, string name)
         {
             PlatformImpl = platformImpl;
+            _name = name;
         }
 
         public Cursor(StandardCursorType cursorType)
-            : this(GetCursorFactory().GetCursor(cursorType))
+            : this(GetCursorFactory().GetCursor(cursorType), cursorType.ToString())
         {
         }
 
-        public Cursor(IBitmap cursor, PixelPoint hotSpot)
-            : this(GetCursorFactory().CreateCursor(cursor.PlatformImpl.Item, hotSpot))
+        public Cursor(Bitmap cursor, PixelPoint hotSpot)
+            : this(GetCursorFactory().CreateCursor(cursor.PlatformImpl.Item, hotSpot), "BitmapCursor")
         {
         }
 
-        public ICursorImpl PlatformImpl { get; }
+        internal ICursorImpl PlatformImpl { get; }
 
         public void Dispose() => PlatformImpl.Dispose();
 
@@ -73,5 +75,10 @@ namespace Avalonia.Input
         {
             return AvaloniaLocator.Current.GetRequiredService<ICursorFactory>();
         }
+
+        public override string ToString()
+        {
+            return _name;
+        }
     }
 }

+ 2 - 1
src/Avalonia.Base/Input/DataFormats.cs

@@ -1,4 +1,5 @@
 using System;
+using System.ComponentModel;
 
 namespace Avalonia.Input
 {
@@ -17,7 +18,7 @@ namespace Avalonia.Input
         /// <summary>
         /// Dataformat for one or more filenames
         /// </summary>
-        [Obsolete("Use DataFormats.Files, this format is supported only on desktop platforms.")]
+        [Obsolete("Use DataFormats.Files, this format is supported only on desktop platforms."), EditorBrowsable(EditorBrowsableState.Never)]
         public static readonly string FileNames = nameof(FileNames);
     }
 }

+ 2 - 1
src/Avalonia.Base/Input/DataObjectExtensions.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
 using Avalonia.Platform.Storage;
 
@@ -25,7 +26,7 @@ namespace Avalonia.Input
         /// <returns>
         /// Collection of file names. If format isn't available, returns null.
         /// </returns>
-        [System.Obsolete("Use GetFiles, this method is supported only on desktop platforms.")]
+        [System.Obsolete("Use GetFiles, this method is supported only on desktop platforms."), EditorBrowsable(EditorBrowsableState.Never)]
         public static IEnumerable<string>? GetFileNames(this IDataObject dataObject)
         {
             return (dataObject.Get(DataFormats.FileNames) as IEnumerable<string>)

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

@@ -25,8 +25,7 @@ namespace Avalonia.Input
             return _target.TranslatePoint(_targetLocation, relativeTo) ?? new Point(0, 0);
         }
 
-        [Unstable]
-        [Obsolete("This constructor might be removed in 12.0. For unit testing, consider using DragDrop.DoDragDrop or IHeadlessWindow.DragDrop.")]
+        [Unstable("This constructor might be removed in 12.0. For unit testing, consider using DragDrop.DoDragDrop or IHeadlessWindow.DragDrop.")]
         public DragEventArgs(RoutedEvent<DragEventArgs> routedEvent, IDataObject data, Interactive target, Point targetLocation, KeyModifiers keyModifiers)
             : base(routedEvent)
         {

+ 41 - 20
src/Avalonia.Base/Input/FocusManager.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
 using Avalonia.Interactivity;
+using Avalonia.Metadata;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Input
@@ -10,6 +11,7 @@ namespace Avalonia.Input
     /// <summary>
     /// Manages focus for the application.
     /// </summary>
+    [PrivateApi]
     public class FocusManager : IFocusManager
     {
         /// <summary>
@@ -29,15 +31,12 @@ namespace Avalonia.Input
                 RoutingStrategies.Tunnel);
         }
 
-        /// <summary>
-        /// Gets the instance of the <see cref="IFocusManager"/>.
-        /// </summary>
-        public static IFocusManager? Instance => AvaloniaLocator.Current.GetService<IFocusManager>();
+        private IInputElement? Current => KeyboardDevice.Instance?.FocusedElement;
 
         /// <summary>
         /// Gets the currently focused <see cref="IInputElement"/>.
         /// </summary>
-        public IInputElement? Current => KeyboardDevice.Instance?.FocusedElement;
+        public IInputElement? GetFocusedElement() => Current;
 
         /// <summary>
         /// Gets the current focus scope.
@@ -54,7 +53,7 @@ namespace Avalonia.Input
         /// <param name="control">The control to focus.</param>
         /// <param name="method">The method by which focus was changed.</param>
         /// <param name="keyModifiers">Any key modifiers active at the time of focus.</param>
-        public void Focus(
+        public bool Focus(
             IInputElement? control, 
             NavigationMethod method = NavigationMethod.Unspecified,
             KeyModifiers keyModifiers = KeyModifiers.None)
@@ -67,7 +66,7 @@ namespace Avalonia.Input
                 if (scope != null)
                 {
                     Scope = scope;
-                    SetFocusedElement(scope, control, method, keyModifiers);
+                    return SetFocusedElement(scope, control, method, keyModifiers);
                 }
             }
             else if (Current != null)
@@ -79,28 +78,29 @@ namespace Avalonia.Input
                         _focusScopes.TryGetValue(scope, out var element) &&
                         element != null)
                     {
-                        Focus(element, method);
-                        return;
+                        return Focus(element, method);
                     }
                 }
 
                 if (Scope is object)
                 {
                     // Couldn't find a focus scope, clear focus.
-                    SetFocusedElement(Scope, null);
+                    return SetFocusedElement(Scope, null);
                 }
             }
+
+            return false;
         }
 
-        public IInputElement? GetFocusedElement(IInputElement e)
+        public void ClearFocus()
         {
-            if (e is IFocusScope scope)
-            {
-                _focusScopes.TryGetValue(scope, out var result);
-                return result;
-            }
+            Focus(null);
+        }
 
-            return null;
+        public IInputElement? GetFocusedElement(IFocusScope scope)
+        {
+            _focusScopes.TryGetValue(scope, out var result);
+            return result;
         }
 
         /// <summary>
@@ -114,7 +114,7 @@ namespace Avalonia.Input
         /// If the specified scope is the current <see cref="Scope"/> then the keyboard focus
         /// will change.
         /// </remarks>
-        public void SetFocusedElement(
+        public bool SetFocusedElement(
             IFocusScope scope,
             IInputElement? element,
             NavigationMethod method = NavigationMethod.Unspecified,
@@ -122,6 +122,11 @@ namespace Avalonia.Input
         {
             scope = scope ?? throw new ArgumentNullException(nameof(scope));
 
+            if (element is not null && !CanFocus(element))
+            {
+                return false;
+            }
+
             if (_focusScopes.TryGetValue(scope, out var existingElement))
             {
                 if (element != existingElement)
@@ -139,6 +144,8 @@ namespace Avalonia.Input
             {
                 KeyboardDevice.Instance?.SetFocusedElement(element, method, keyModifiers);
             }
+
+            return true;
         }
 
         /// <summary>
@@ -180,6 +187,20 @@ namespace Avalonia.Input
 
         public static bool GetIsFocusScope(IInputElement e) => e is IFocusScope;
 
+        /// <summary>
+        /// Public API customers should use TopLevel.GetTopLevel(control).FocusManager.
+        /// But since we have split projects, we can't access TopLevel from Avalonia.Base.
+        /// That's why we need this helper method instead.
+        /// </summary>
+        internal static FocusManager? GetFocusManager(IInputElement? element)
+        {
+            // Element might not be a visual, and not attached to the root.
+            // But IFocusManager is always expected to be a FocusManager. 
+            return (FocusManager?)((element as Visual)?.VisualRoot as IInputRoot)?.FocusManager
+                   // In our unit tests some elements might not have a root. Remove when we migrate to headless tests.
+                ?? (FocusManager?)AvaloniaLocator.Current.GetService<IFocusManager>();
+        }
+        
         /// <summary>
         /// Checks if the specified element can be focused.
         /// </summary>
@@ -232,7 +253,7 @@ namespace Avalonia.Input
                 {
                     if (element is IInputElement inputElement && CanFocus(inputElement))
                     {
-                        Instance?.Focus(inputElement, NavigationMethod.Pointer, ev.KeyModifiers);
+                        inputElement.Focus(NavigationMethod.Pointer, ev.KeyModifiers);
 
                         break;
                     }
@@ -242,6 +263,6 @@ namespace Avalonia.Input
             }
         }
 
-        private static bool IsVisible(IInputElement e) => (e as Visual)?.IsVisible ?? true;
+        private static bool IsVisible(IInputElement e) => (e as Visual)?.IsEffectivelyVisible ?? true;
     }
 }

+ 1 - 2
src/Avalonia.Base/Input/IAccessKeyHandler.cs

@@ -5,8 +5,7 @@ namespace Avalonia.Input
     /// <summary>
     /// Defines the interface for classes that handle access keys for a window.
     /// </summary>
-    [Unstable]
-    public interface IAccessKeyHandler
+    internal interface IAccessKeyHandler
     {
         /// <summary>
         /// Gets or sets the window's main menu.

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