ソースを参照

Merge remote-tracking branch 'origin/master' into xunit-ext

# Conflicts:
#	src/Headless/Avalonia.Headless/HeadlessPlatformStubs.cs
#	src/Headless/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs
#	src/Headless/Avalonia.Headless/HeadlessWindowImpl.cs
Max Katz 2 年 前
コミット
2d7c55ccdc
100 ファイル変更1825 行追加650 行削除
  1. 1 0
      .gitignore
  2. 5 0
      .ncrunch/Avalonia.Generators.Tests.v3.ncrunchproject
  3. 5 0
      .ncrunch/Generators.Sandbox.v3.ncrunchproject
  4. 5 0
      .ncrunch/SafeAreaDemo.Android.v3.ncrunchproject
  5. 5 0
      .ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject
  6. 5 0
      .ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject
  7. 5 0
      .ncrunch/SafeAreaDemo.v3.ncrunchproject
  8. 2 2
      .nuke/build.schema.json
  9. 7 5
      Avalonia.Desktop.slnf
  10. 54 1
      Avalonia.sln
  11. 1 1
      Directory.Build.targets
  12. 1 0
      azure-pipelines-integrationtests.yml
  13. 16 1
      build/SourceGenerators.props
  14. 12 0
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  15. 46 0
      native/Avalonia.Native/src/OSX/AvnTextInputMethod.h
  16. 41 0
      native/Avalonia.Native/src/OSX/AvnTextInputMethod.mm
  17. 20 0
      native/Avalonia.Native/src/OSX/AvnTextInputMethodDelegate.h
  18. 2 4
      native/Avalonia.Native/src/OSX/AvnView.h
  19. 103 17
      native/Avalonia.Native/src/OSX/AvnView.mm
  20. 1 1
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  21. 6 0
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  22. 10 0
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  23. 2 0
      native/Avalonia.Native/src/OSX/WindowImpl.h
  24. 8 4
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  25. 207 134
      native/Avalonia.Native/src/OSX/platformthreading.mm
  26. 9 5
      nukebuild/Build.cs
  27. 24 0
      nukebuild/Helpers.cs
  28. 171 0
      nukebuild/RefAssemblyGenerator.cs
  29. 3 10
      nukebuild/_build.csproj
  30. 5 0
      nukebuild/numerge.config
  31. 9 1
      packages/Avalonia/Avalonia.csproj
  32. 1 0
      packages/Avalonia/Avalonia.props
  33. 4 0
      readme.md
  34. 2 2
      samples/BindingDemo/MainWindow.xaml
  35. 1 1
      samples/ControlCatalog.Android/MainActivity.cs
  36. 2 0
      samples/ControlCatalog.Android/Properties/AndroidManifest.xml
  37. 4 0
      samples/ControlCatalog.Android/Resources/values-night/colors.xml
  38. 3 11
      samples/ControlCatalog/ControlCatalog.csproj
  39. 1 4
      samples/ControlCatalog/MainView.xaml
  40. 2 2
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  41. 56 18
      samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
  42. 35 0
      samples/ControlCatalog/Pages/ColorPickerPage.xaml
  43. 1 1
      samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs
  44. 1 1
      samples/ControlCatalog/Pages/CompositionPage.axaml.cs
  45. 2 2
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
  46. 3 2
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
  47. 2 2
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  48. 1 1
      samples/ControlCatalog/Pages/CursorPage.xaml
  49. 4 4
      samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
  50. 1 1
      samples/ControlCatalog/Pages/DataGridPage.xaml
  51. 2 2
      samples/ControlCatalog/Pages/DataGridPage.xaml.cs
  52. 2 2
      samples/ControlCatalog/Pages/DialogsPage.xaml
  53. 8 8
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  54. 6 2
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  55. 16 2
      samples/ControlCatalog/Pages/FlyoutsPage.axaml
  56. 1 7
      samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs
  57. 1 1
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
  58. 2 9
      samples/ControlCatalog/Pages/LabelsPage.axaml.cs
  59. 1 1
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  60. 3 3
      samples/ControlCatalog/Pages/MenuPage.xaml
  61. 4 4
      samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs
  62. 3 3
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  63. 1 1
      samples/ControlCatalog/Pages/RefreshContainerPage.axaml
  64. 3 11
      samples/ControlCatalog/Pages/RefreshContainerPage.axaml.cs
  65. 3 10
      samples/ControlCatalog/Pages/RelativePanelPage.axaml.cs
  66. 0 222
      samples/ControlCatalog/Pages/ScrollSnapPage.xaml
  67. 0 68
      samples/ControlCatalog/Pages/ScrollSnapPage.xaml.cs
  68. 261 29
      samples/ControlCatalog/Pages/ScrollViewerPage.xaml
  69. 37 0
      samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs
  70. 1 1
      samples/ControlCatalog/Pages/TabControlPage.xaml
  71. 1 1
      samples/ControlCatalog/Pages/TabStripPage.xaml
  72. 8 12
      samples/ControlCatalog/Pages/ThemePage.axaml.cs
  73. 1 1
      samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml
  74. 1 1
      samples/ControlCatalog/Pages/TreeViewPage.xaml
  75. 7 0
      samples/Generators.Sandbox/App.xaml
  76. 20 0
      samples/Generators.Sandbox/App.xaml.cs
  77. 10 0
      samples/Generators.Sandbox/Controls/CustomTextBox.cs
  78. 45 0
      samples/Generators.Sandbox/Controls/SignUpView.xaml
  79. 54 0
      samples/Generators.Sandbox/Controls/SignUpView.xaml.cs
  80. 28 0
      samples/Generators.Sandbox/Generators.Sandbox.csproj
  81. 15 0
      samples/Generators.Sandbox/Program.cs
  82. 70 0
      samples/Generators.Sandbox/ViewModels/SignUpViewModel.cs
  83. 9 0
      samples/Generators.Sandbox/Views/SignUpView.xaml
  84. 28 0
      samples/Generators.Sandbox/Views/SignUpView.xaml.cs
  85. 23 3
      samples/IntegrationTestApp/MainWindow.axaml
  86. 7 1
      samples/IntegrationTestApp/MainWindow.axaml.cs
  87. 18 6
      samples/IntegrationTestApp/ShowWindowTest.axaml
  88. 40 0
      samples/RenderDemo/Pages/AnimationsPage.xaml
  89. 1 1
      samples/RenderDemo/Pages/RenderTargetBitmapPage.cs
  90. BIN
      samples/SafeAreaDemo.Android/Icon.png
  91. 11 0
      samples/SafeAreaDemo.Android/MainActivity.cs
  92. 5 0
      samples/SafeAreaDemo.Android/Properties/AndroidManifest.xml
  93. 13 0
      samples/SafeAreaDemo.Android/Resources/drawable/splash_screen.xml
  94. 4 0
      samples/SafeAreaDemo.Android/Resources/values/colors.xml
  95. 17 0
      samples/SafeAreaDemo.Android/Resources/values/styles.xml
  96. 24 0
      samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj
  97. 30 0
      samples/SafeAreaDemo.Android/SplashActivity.cs
  98. 21 0
      samples/SafeAreaDemo.Desktop/Program.cs
  99. 24 0
      samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj
  100. 18 0
      samples/SafeAreaDemo.Desktop/app.manifest

+ 1 - 0
.gitignore

@@ -102,6 +102,7 @@ csx
 AppPackages/
 
 # NCrunch
+.NCrunch_*/
 _NCrunch_*/
 *.ncrunchsolution.user
 nCrunchTemp_*

+ 5 - 0
.ncrunch/Avalonia.Generators.Tests.v3.ncrunchproject

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

+ 5 - 0
.ncrunch/Generators.Sandbox.v3.ncrunchproject

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

+ 5 - 0
.ncrunch/SafeAreaDemo.Android.v3.ncrunchproject

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

+ 5 - 0
.ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject

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

+ 5 - 0
.ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject

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

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

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

+ 2 - 2
.nuke/build.schema.json

@@ -84,11 +84,11 @@
               "GenerateCppHeaders",
               "Package",
               "RunCoreLibsTests",
-              "RunDesignerTests",
               "RunHtmlPreviewerTests",
               "RunLeakTests",
               "RunRenderTests",
               "RunTests",
+              "RunToolsTests",
               "ZipFiles"
             ]
           }
@@ -123,11 +123,11 @@
               "GenerateCppHeaders",
               "Package",
               "RunCoreLibsTests",
-              "RunDesignerTests",
               "RunHtmlPreviewerTests",
               "RunLeakTests",
               "RunRenderTests",
               "RunTests",
+              "RunToolsTests",
               "ZipFiles"
             ]
           }

+ 7 - 5
Avalonia.Desktop.slnf

@@ -8,9 +8,9 @@
       "samples\\GpuInterop\\GpuInterop.csproj",
       "samples\\IntegrationTestApp\\IntegrationTestApp.csproj",
       "samples\\MiniMvvm\\MiniMvvm.csproj",
+      "samples\\ReactiveUIDemo\\ReactiveUIDemo.csproj",
       "samples\\SampleControls\\ControlSamples.csproj",
       "samples\\Sandbox\\Sandbox.csproj",
-      "samples\\ReactiveUIDemo\\ReactiveUIDemo.csproj",
       "src\\Avalonia.Base\\Avalonia.Base.csproj",
       "src\\Avalonia.Build.Tasks\\Avalonia.Build.Tasks.csproj",
       "src\\Avalonia.Controls.ColorPicker\\Avalonia.Controls.ColorPicker.csproj",
@@ -38,12 +38,14 @@
       "src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj",
       "src\\Markup\\Avalonia.Markup\\Avalonia.Markup.csproj",
       "src\\Skia\\Avalonia.Skia\\Avalonia.Skia.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.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",
       "tests\\Avalonia.Base.UnitTests\\Avalonia.Base.UnitTests.csproj",
       "tests\\Avalonia.Benchmarks\\Avalonia.Benchmarks.csproj",
       "tests\\Avalonia.Controls.DataGrid.UnitTests\\Avalonia.Controls.DataGrid.UnitTests.csproj",
@@ -63,4 +65,4 @@
       "tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj"
     ]
   }
-}
+}

+ 54 - 1
Avalonia.sln

@@ -244,7 +244,21 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepe
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepeater.UnitTests", "tests\Avalonia.Controls.ItemsRepeater.UnitTests\Avalonia.Controls.ItemsRepeater.UnitTests.csproj", "{F4E36AA8-814E-4704-BC07-291F70F45193}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Fonts.Inter", "src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}"
+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}") = "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}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.Desktop", "samples\SafeAreaDemo.Desktop\SafeAreaDemo.Desktop.csproj", "{4CDAD037-34A2-4CCF-A03A-C6C7B988A572}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeAreaDemo.iOS", "samples\SafeAreaDemo.iOS\SafeAreaDemo.iOS.csproj", "{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Headless", "Headless", "{FF237916-7150-496B-89ED-6CA3292896E7}"
 EndProject
@@ -579,6 +593,14 @@ Global
 		{F4E36AA8-814E-4704-BC07-291F70F45193}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{F4E36AA8-814E-4704-BC07-291F70F45193}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{F4E36AA8-814E-4704-BC07-291F70F45193}.Release|Any CPU.Build.0 = Release|Any CPU
+		{DDA28789-C21A-4654-86CE-D01E81F095C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{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
@@ -591,6 +613,30 @@ Global
 		{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
+		{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
+		{6B60A970-D5D2-49C2-8BAB-F9C7973B74B6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{22E3BC08-EAF7-4889-BDC4-B4D3046C4E2D}.Release|Any CPU.Deploy.0 = Release|Any CPU
+		{4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4CDAD037-34A2-4CCF-A03A-C6C7B988A572}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FC956F9A-4C3A-4A1A-ACDD-BB54DCB661DD}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{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
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -662,6 +708,13 @@ Global
 		{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}
+		{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}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

+ 1 - 1
Directory.Build.targets

@@ -1,5 +1,5 @@
 <Project>
-  <PropertyGroup Condition="$(NETCoreSdkVersion.StartsWith('7.0'))">
+  <PropertyGroup Condition="$([MSBuild]::VersionGreaterThanOrEquals($(NETCoreSdkVersion), '7.0'))">
     <DefineConstants>$(DefineConstants);NET7SDK</DefineConstants>
   </PropertyGroup>  
 </Project>

+ 1 - 0
azure-pipelines-integrationtests.yml

@@ -24,6 +24,7 @@ jobs:
       fi
       sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
       pkill node
+      pkill testmanagerd
       appium > appium.out &
       pkill IntegrationTestApp
       ./build.sh CompileNative

+ 16 - 1
build/SourceGenerators.props

@@ -1,5 +1,10 @@
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
+  <PropertyGroup>
+    <IncludeDevGenerators Condition="'$(IncludeDevGenerators)' == ''">true</IncludeDevGenerators>
+    <IncludeAvaloniaGenerators Condition="'$(IncludeAvaloniaGenerators)' == ''">false</IncludeAvaloniaGenerators>
+  </PropertyGroup>
+
+  <ItemGroup Condition="'$(IncludeDevGenerators)' == 'true'">
     <ProjectReference 
       Include="$(MSBuildThisFileDirectory)/../src/tools/DevGenerators/DevGenerators.csproj"
       OutputItemType="Analyzer" 
@@ -7,4 +12,14 @@
       PrivateAssets="all" />
     <Compile Include="$(MSBuildThisFileDirectory)/../src/Shared/SourceGeneratorAttributes.cs" />
   </ItemGroup>
+
+  <ItemGroup Condition="'$(IncludeAvaloniaGenerators)' == 'true'">
+    <ProjectReference
+      Include="../../src/tools/Avalonia.Generators/Avalonia.Generators.csproj"
+      OutputItemType="Analyzer"
+      ReferenceOutputAssembly="false"
+      PrivateAssets="all" />
+  </ItemGroup>
+  <Import Project="$(MSBuildThisFileDirectory)/../src/tools/Avalonia.Generators/Avalonia.Generators.props"
+          Condition="'$(IncludeDevGenerators)' == 'true'" />
 </Project>

+ 12 - 0
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@@ -44,6 +44,9 @@
 		5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; };
 		5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
 		855EDC9F28C6546F00807998 /* PlatformBehaviorInhibition.mm in Sources */ = {isa = PBXBuildFile; fileRef = 855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */; };
+		8D2F3512292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */; };
+		8D300D65292D0A6800320C49 /* AvnTextInputMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */; };
+		8D300D69292E1E5D00320C49 /* AvnTextInputMethod.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8D300D68292E1E5D00320C49 /* AvnTextInputMethod.mm */; };
 		AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
 		AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; };
 		AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
@@ -97,6 +100,9 @@
 		5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = "<group>"; };
 		5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; };
 		855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformBehaviorInhibition.mm; sourceTree = "<group>"; };
+		8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnTextInputMethodDelegate.h; sourceTree = "<group>"; };
+		8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnTextInputMethod.h; sourceTree = "<group>"; };
+		8D300D68292E1E5D00320C49 /* AvnTextInputMethod.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnTextInputMethod.mm; sourceTree = "<group>"; };
 		AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
 		AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
 		AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -143,6 +149,9 @@
 			isa = PBXGroup;
 			children = (
 				855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */,
+				8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */,
+				8D300D68292E1E5D00320C49 /* AvnTextInputMethod.mm */,
+				8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */,
 				BC11A5BC2608D58F0017BAD0 /* automation.h */,
 				BC11A5BD2608D58F0017BAD0 /* automation.mm */,
 				1A1852DB23E05814008F0DED /* deadlock.mm */,
@@ -213,6 +222,8 @@
 				1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */,
 				183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */,
 				18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */,
+				8D300D65292D0A6800320C49 /* AvnTextInputMethod.h in Headers */,
+				8D2F3512292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h in Headers */,
 				18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */,
 				18391ED5F611FF62C45F196D /* AvnView.h in Headers */,
 				18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */,
@@ -293,6 +304,7 @@
 				37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
 				855EDC9F28C6546F00807998 /* PlatformBehaviorInhibition.mm in Sources */,
 				520624B322973F4100C4DCEF /* menu.mm in Sources */,
+				8D300D69292E1E5D00320C49 /* AvnTextInputMethod.mm in Sources */,
 				37A517B32159597E00FBA241 /* Screens.mm in Sources */,
 				1AFD334123E03C4F0042899B /* controlhost.mm in Sources */,
 				1A465D10246AB61600C5858B /* dnd.mm in Sources */,

+ 46 - 0
native/Avalonia.Native/src/OSX/AvnTextInputMethod.h

@@ -0,0 +1,46 @@
+//
+//  AvnTextInputMethod.h
+//  Avalonia.Native.OSX
+//
+//  Created by Benedikt Stebner on 22.11.22.
+//  Copyright © 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AvnTextInputMethod_h
+#define AvnTextInputMethod_h
+
+#import <Foundation/Foundation.h>
+
+#include "com.h"
+#include "comimpl.h"
+#include "avalonia-native.h"
+#import "AvnTextInputMethodDelegate.h"
+
+class AvnTextInputMethod: public virtual ComObject, public virtual IAvnTextInputMethod{
+private:
+    id<AvnTextInputMethodDelegate> _inputMethodDelegate;
+public:
+    FORWARD_IUNKNOWN()
+    
+    BEGIN_INTERFACE_MAP()
+    INTERFACE_MAP_ENTRY(IAvnTextInputMethod, IID_IAvnTextInputMethod)
+    END_INTERFACE_MAP()
+    
+    virtual ~AvnTextInputMethod();
+    
+    AvnTextInputMethod(id<AvnTextInputMethodDelegate> inputMethodDelegate);
+    
+    bool IsActive ();
+    
+    HRESULT SetClient (IAvnTextInputMethodClient* client) override;
+    
+    virtual void Reset () override;
+    
+    virtual void SetCursorRect (AvnRect rect) override;
+    
+    virtual void SetSurroundingText (char* text, int anchorOffset, int cursorOffset) override;
+    
+public:
+    ComPtr<IAvnTextInputMethodClient> Client;
+};
+#endif /* AvnTextInputMethod_h */

+ 41 - 0
native/Avalonia.Native/src/OSX/AvnTextInputMethod.mm

@@ -0,0 +1,41 @@
+//
+//  AvnTextInputMethod.mm
+//  Avalonia.Native.OSX
+//
+//  Created by Benedikt Stebner on 23.11.22.
+//  Copyright © 2022 Avalonia. All rights reserved.
+//
+
+#include "AvnTextInputMethod.h"
+
+AvnTextInputMethod::~AvnTextInputMethod() {
+    Client = nullptr;
+}
+
+AvnTextInputMethod::AvnTextInputMethod(id<AvnTextInputMethodDelegate> inputMethodDelegate) {
+    _inputMethodDelegate = inputMethodDelegate;
+}
+
+bool AvnTextInputMethod::IsActive() {
+    return Client != nullptr;
+}
+
+HRESULT AvnTextInputMethod::SetClient(IAvnTextInputMethodClient *client) {
+    START_COM_CALL;
+    
+    Client = client;
+    
+    return S_OK;
+}
+
+void AvnTextInputMethod::Reset() {
+}
+
+void AvnTextInputMethod::SetSurroundingText(char* text, int anchorOffset, int cursorOffset) {
+    [_inputMethodDelegate setText:[NSString stringWithUTF8String:text]];
+    [_inputMethodDelegate setSelection: anchorOffset : cursorOffset];
+}
+
+void AvnTextInputMethod::SetCursorRect(AvnRect rect) {
+    [_inputMethodDelegate setCursorRect: rect];
+}

+ 20 - 0
native/Avalonia.Native/src/OSX/AvnTextInputMethodDelegate.h

@@ -0,0 +1,20 @@
+//
+//  AvnTextInputMethodHost.h
+//  Avalonia.Native.OSX
+//
+//  Created by Benedikt Stebner on 24.11.22.
+//  Copyright © 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AvnTextInputMethodHost_h
+#define AvnTextInputMethodHost_h
+
+@protocol AvnTextInputMethodDelegate
+@required
+-(void) setText:(NSString* _Nonnull) text;
+-(void) setCursorRect:(AvnRect) cursorRect;
+-(void) setSelection: (int) start : (int) end;
+
+@end
+
+#endif /* AvnTextInputMethodHost_h */

+ 2 - 4
native/Avalonia.Native/src/OSX/AvnView.h

@@ -5,8 +5,6 @@
 #pragma once
 #import <Foundation/Foundation.h>
 
-
-#import <Foundation/Foundation.h>
 #import <AppKit/AppKit.h>
 #include "common.h"
 #include "WindowImpl.h"
@@ -14,7 +12,7 @@
 
 @class AvnAccessibilityElement;
 
-@interface AvnView : NSView<NSTextInputClient, NSDraggingDestination>
+@interface AvnView : NSView<NSTextInputClient, NSDraggingDestination, AvnTextInputMethodDelegate>
 -(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
 -(NSEvent* _Nonnull) lastMouseDownEvent;
 -(AvnPoint) translateLocalPoint:(AvnPoint)pt;
@@ -24,4 +22,4 @@
 -(AvnPlatformResizeReason) getResizeReason;
 -(void) setResizeReason:(AvnPlatformResizeReason)reason;
 + (AvnPoint)toAvnPoint:(CGPoint)p;
-@end
+@end

+ 103 - 17
native/Avalonia.Native/src/OSX/AvnView.mm

@@ -20,6 +20,10 @@
     NSObject<IRenderTarget>* _renderTarget;
     AvnPlatformResizeReason _resizeReason;
     AvnAccessibilityElement* _accessibilityChild;
+    NSRect _cursorRect;
+    NSMutableAttributedString* _text;
+    NSRange _selectedRange;
+    NSRange _markedRange;
 }
 
 - (void)onClosed
@@ -55,6 +59,11 @@
     [self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]];
 
     _modifierState = AvnInputModifiersNone;
+    
+    _text = [[NSMutableAttributedString alloc] initWithString:@""];
+    _markedRange = NSMakeRange(0, 0);
+    _selectedRange = NSMakeRange(0, 0);
+    
     return self;
 }
 
@@ -517,9 +526,13 @@
 
 - (void)keyDown:(NSEvent *)event
 {
-    [self keyboardEvent:event withType:KeyDown];
+    _lastKeyHandled = false;
+        
     [[self inputContext] handleEvent:event];
-    [super keyDown:event];
+    
+    if(!_lastKeyHandled){
+        [self keyboardEvent:event withType:KeyDown];
+    }
 }
 
 - (void)keyUp:(NSEvent *)event
@@ -528,6 +541,10 @@
     [super keyUp:event];
 }
 
+- (void) doCommandBySelector:(SEL)selector{
+    
+}
+
 - (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod
 {
     unsigned int rv = 0;
@@ -557,27 +574,52 @@
 
 - (BOOL)hasMarkedText
 {
-    return _lastKeyHandled;
+    return _markedRange.length > 0;
 }
 
 - (NSRange)markedRange
 {
-    return NSMakeRange(NSNotFound, 0);
+    return _markedRange;
 }
 
 - (NSRange)selectedRange
 {
-    return NSMakeRange(NSNotFound, 0);
+    return _selectedRange;
 }
 
 - (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
 {
-
+    _lastKeyHandled = true;
+    
+    NSString* markedText;
+        
+    if([string isKindOfClass:[NSAttributedString class]])
+    {
+        markedText = [string string];
+    }
+    else
+    {
+        markedText = (NSString*) string;
+    }
+    
+    _markedRange = NSMakeRange(_selectedRange.location, [markedText length]);
+        
+    if(_parent->InputMethod->IsActive()){
+        _parent->InputMethod->Client->SetPreeditText((char*)[markedText UTF8String]);
+    }
 }
 
 - (void)unmarkText
 {
-
+    if(_parent->InputMethod->IsActive()){
+        _parent->InputMethod->Client->SetPreeditText(nullptr);
+    }
+    
+    _markedRange = NSMakeRange(_selectedRange.location, 0);
+    
+    if([self inputContext]) {
+        [[self inputContext] discardMarkedText];
+    }
 }
 
 - (NSArray<NSString *> *)validAttributesForMarkedText
@@ -587,30 +629,52 @@
 
 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
 {
-    return [NSAttributedString new];
+    if(actualRange){
+        range = *actualRange;
+    }
+    
+    NSAttributedString* subString = [_text attributedSubstringFromRange:range];
+    
+    return subString;
 }
 
 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
 {
-    if(!_lastKeyHandled)
+    if(_parent == nullptr){
+        return;
+    }
+    
+    NSString* text;
+        
+    if([string isKindOfClass:[NSAttributedString class]])
     {
-        if(_parent != nullptr)
-        {
-            _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
-        }
+        text = [string string];
+    }
+    else
+    {
+        text = (NSString*) string;
     }
+    
+    [self unmarkText];
+        
+    uint32_t timestamp = static_cast<uint32_t>([NSDate timeIntervalSinceReferenceDate] * 1000);
+        
+    _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(timestamp, [text UTF8String]);
+    
 }
 
 - (NSUInteger)characterIndexForPoint:(NSPoint)point
 {
-    return 0;
+    return NSNotFound;
 }
 
 - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
 {
-    CGRect result = { 0 };
-
-    return result;
+    if(!_parent->InputMethod->IsActive()){
+        return NSZeroRect;
+    }
+    
+    return _cursorRect;
 }
 
 - (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id <NSDraggingInfo>)info
@@ -715,4 +779,26 @@
     return [[self accessibilityChild] accessibilityFocusedUIElement];
 }
 
+- (void) setText:(NSString *)text{
+    [[_text mutableString] setString:text];
+}
+
+- (void) setSelection:(int)start :(int)end{
+    _selectedRange = NSMakeRange(start, end - start);
+}
+
+- (void) setCursorRect:(AvnRect)rect{
+    NSRect cursorRect = ToNSRect(rect);
+    NSRect windowRectOnScreen = [[self window] convertRectToScreen:self.frame];
+    
+    windowRectOnScreen.size = cursorRect.size;
+    windowRectOnScreen.origin = NSMakePoint(windowRectOnScreen.origin.x + cursorRect.origin.x, windowRectOnScreen.origin.y + self.frame.size.height - cursorRect.origin.y - cursorRect.size.height);
+    
+    _cursorRect = windowRectOnScreen;
+    
+    if([self inputContext]) {
+        [[self inputContext] invalidateCharacterCoordinates];
+    }
+}
+
 @end

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

@@ -394,7 +394,7 @@
 
 - (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame
 {
-    return true;
+    return _parent->CanZoom();
 }
 
 -(void)windowDidResignKey:(NSNotification *)notification

+ 6 - 0
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@@ -8,6 +8,7 @@
 
 #include "rendertarget.h"
 #include "INSWindowHolder.h"
+#include "AvnTextInputMethod.h"
 
 @class AutoFitContentView;
 @class AvnMenu;
@@ -103,7 +104,11 @@ BEGIN_INTERFACE_MAP()
     id<AvnWindowProtocol> GetWindowProtocol ();
                            
     virtual void BringToFront ();
+                           
+    virtual HRESULT GetInputMethod(IAvnTextInputMethod **retOut) override;
 
+    virtual bool CanZoom() { return false; }
+                           
 protected:
     virtual NSWindowStyleMask CalculateStyleMask() = 0;
     virtual void UpdateStyle();
@@ -130,6 +135,7 @@ public:
     NSObject <IRenderTarget> *renderTarget;
     NSWindow * Window;
     ComPtr<IAvnWindowBaseEvents> BaseEvents;
+    ComPtr<AvnTextInputMethod> InputMethod;
     AvnView *View;
 };
 

+ 10 - 0
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@@ -15,6 +15,7 @@
 #import "WindowProtocol.h"
 #import "WindowInterfaces.h"
 #include "WindowBaseImpl.h"
+#include "AvnTextInputMethod.h"
 
 
 WindowBaseImpl::~WindowBaseImpl() {
@@ -29,6 +30,7 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl,
     _glContext = gl;
     renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl];
     View = [[AvnView alloc] initWithParent:this];
+    InputMethod = new AvnTextInputMethod(View);
     StandardContainer = [[AutoFitContentView new] initWithContent:View];
 
     lastPositionSet = { 0, 0 };
@@ -605,6 +607,14 @@ void WindowBaseImpl::BringToFront()
     // do nothing.
 }
 
+HRESULT WindowBaseImpl::GetInputMethod(IAvnTextInputMethod **retOut) {
+    START_COM_CALL;
+
+    *retOut = InputMethod;
+
+    return S_OK;
+}
+
 extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)
 {
     @autoreleasepool

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

@@ -97,6 +97,8 @@ BEGIN_INTERFACE_MAP()
     
     bool CanBecomeKeyWindow ();
 
+    bool CanZoom() override { return _isEnabled && _canResize; }
+    
 protected:
     virtual NSWindowStyleMask CalculateStyleMask() override;
     void UpdateStyle () override;

+ 8 - 4
native/Avalonia.Native/src/OSX/WindowImpl.mm

@@ -281,10 +281,13 @@ HRESULT WindowImpl::SetDecorations(SystemDecorations value) {
 
             case SystemDecorationsFull:
                 [Window setHasShadow:YES];
-                [Window setTitleVisibility:NSWindowTitleVisible];
-                [Window setTitlebarAppearsTransparent:NO];
                 [Window setTitle:_lastTitle];
 
+                if (!_isClientAreaExtended) {
+                    [Window setTitleVisibility:NSWindowTitleVisible];
+                    [Window setTitlebarAppearsTransparent:NO];
+                }
+
                 if (currentWindowState == Maximized) {
                     auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
 
@@ -611,7 +614,8 @@ void WindowImpl::UpdateStyle() {
     }
 
     bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
-    bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull;
+    bool hasTrafficLights = (_decorations == SystemDecorationsFull) &&
+        (_isClientAreaExtended ? wantsChrome : true);
     
     NSButton* closeButton = [Window standardWindowButton:NSWindowCloseButton];
     NSButton* miniaturizeButton = [Window standardWindowButton:NSWindowMiniaturizeButton];
@@ -622,5 +626,5 @@ void WindowImpl::UpdateStyle() {
     [miniaturizeButton setHidden:!hasTrafficLights];
     [miniaturizeButton setEnabled:_isEnabled];
     [zoomButton setHidden:!hasTrafficLights];
-    [zoomButton setEnabled:_isEnabled && _canResize];
+    [zoomButton setEnabled:CanZoom()];
 }

+ 207 - 134
native/Avalonia.Native/src/OSX/platformthreading.mm

@@ -1,193 +1,266 @@
 #include "common.h"
 
 class PlatformThreadingInterface;
+
+
+class LoopCancellation : public ComSingleObject<IAvnLoopCancellation, &IID_IAvnLoopCancellation>
+{
+public:
+    FORWARD_IUNKNOWN()
+    
+    bool Running = false;
+    bool Cancelled = false;
+    bool IsApp = false;
+
+    virtual void Cancel() override
+    {
+        Cancelled = true;
+        if(Running)
+        {
+            Running = false;
+            if(![NSThread isMainThread])
+            {
+                AddRef();
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    if(Release() == 0)
+                        return;
+                    Cancel();
+                });
+                return;
+            };
+            if(IsApp)
+                [NSApp stop:nil];
+            else
+            {
+                // Wakeup the event loop
+                NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
+                                                    location:NSMakePoint(0, 0)
+                                               modifierFlags:0
+                                                   timestamp:0
+                                                windowNumber:0
+                                                     context:nil
+                                                     subtype:0
+                                                       data1:0
+                                                       data2:0];
+                [NSApp postEvent:event atStart:YES];
+            }
+        }
+    };
+};
+
+// CFRunLoopTimerSetNextFireDate docs recommend to "create a repeating timer with an initial
+// firing time in the distant future (or the initial firing time) and a very large repeat
+// interval—on the order of decades or more"
+static double distantFutureInterval = (double)50*365*24*3600;
+
 @interface Signaler : NSObject
--(void) setParent: (PlatformThreadingInterface*)parent;
--(void) signal: (int) priority;
+-(void) setEvents:(IAvnPlatformThreadingInterfaceEvents*) events;
+-(void) updateTimer:(int)ms;
 -(Signaler*) init;
+-(void) destroyObserver;
+-(void) signal;
 @end
 
-@implementation ActionCallback
+@implementation Signaler
 {
-    ComPtr<IAvnActionCallback> _callback;
+    ComPtr<IAvnPlatformThreadingInterfaceEvents> _events;
+    bool _wakeupDelegateSent;
+    bool _signaled;
+    bool _backgroundProcessingRequested;
+    CFRunLoopObserverRef _observer;
+    CFRunLoopTimerRef _timer;
+}
 
+- (void) checkSignaled
+{
+    bool signaled;
+    @synchronized (self) {
+        signaled = _signaled;
+        _signaled = false;
+    }
+    if(signaled)
+    {
+        _events->Signaled();
+    }
 }
-- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback
+
+- (Signaler*) init
 {
-    _callback = callback;
+    _observer = CFRunLoopObserverCreateWithHandler(nil,
+                                                   kCFRunLoopBeforeSources
+                                                   | kCFRunLoopAfterWaiting
+                                                   | kCFRunLoopBeforeWaiting
+                                                   ,
+                                                   true, 0,
+                                                   ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
+        if(activity == kCFRunLoopBeforeWaiting)
+        {
+            bool triggerProcessing;
+            @synchronized (self) {
+                triggerProcessing = self->_backgroundProcessingRequested;
+                self->_backgroundProcessingRequested = false;
+            }
+            if(triggerProcessing)
+                self->_events->ReadyForBackgroundProcessing();
+        }
+        [self checkSignaled];
+    });
+    CFRunLoopAddObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes);
+    
+    
+    _timer = CFRunLoopTimerCreateWithHandler(nil, CFAbsoluteTimeGetCurrent() + distantFutureInterval, distantFutureInterval, 0, 0, ^(CFRunLoopTimerRef timer) {
+        self->_events->Timer();
+    });
+    
+    CFRunLoopAddTimer(CFRunLoopGetMain(), _timer, kCFRunLoopCommonModes);
+    
     return self;
 }
 
-- (void) action
+- (void) destroyObserver
 {
-    _callback->Run();
+    if(_observer != nil)
+    {
+        CFRunLoopObserverInvalidate(_observer);
+        CFRelease(_observer);
+        _observer = nil;
+    }
+    
+    if(_timer != nil)
+    {
+        CFRunLoopTimerInvalidate(_timer);
+        CFRelease(_timer);
+        _timer = nil;
+    }
 }
 
+-(void) updateTimer:(int)ms
+{
+    if(_timer == nil)
+        return;
+    double interval = ms < 0 ? distantFutureInterval : ((double)ms / 1000);
+    CFRunLoopTimerSetTolerance(_timer, 0);
+    CFRunLoopTimerSetNextFireDate(_timer, CFAbsoluteTimeGetCurrent() + interval);
+}
 
-@end
+- (void) setEvents: (IAvnPlatformThreadingInterfaceEvents*) events
+{
+    _events = events;
+}
 
-class TimerWrapper : public ComUnknownObject
+- (void) signal
 {
-    NSTimer* _timer;
-public:
-    TimerWrapper(IAvnActionCallback* callback, int ms)
-    {
-        auto cb = [[ActionCallback alloc] initWithCallback:callback];
-        _timer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(double)ms/1000 target:cb selector:@selector(action) userInfo:nullptr repeats:true];
+    @synchronized (self) {
+        if(_signaled)
+            return;
+        _signaled = true;
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [self checkSignaled];
+        });
+        CFRunLoopWakeUp(CFRunLoopGetMain());
     }
-                  
-    virtual ~TimerWrapper()
-    {
-         [_timer invalidate];
+}
+
+- (void) requestBackgroundProcessing
+{
+    @synchronized (self) {
+        if(_backgroundProcessingRequested)
+            return;
+        _backgroundProcessingRequested = true;
+        dispatch_async(dispatch_get_main_queue(), ^{
+            // This is needed to wakeup the loop if we are called from inside of BeforeWait hook
+        });
     }
-};
+    
+        
+}
 
+@end
 
 
 class PlatformThreadingInterface : public ComSingleObject<IAvnPlatformThreadingInterface, &IID_IAvnPlatformThreadingInterface>
 {
 private:
+    ComPtr<IAvnPlatformThreadingInterfaceEvents> _events;
     Signaler* _signaler;
-    bool _wasRunningAtLeastOnce = false;
-    
-    class LoopCancellation : public ComSingleObject<IAvnLoopCancellation, &IID_IAvnLoopCancellation>
-    {
-    public:
-        FORWARD_IUNKNOWN()
-        
-        bool Running = false;
-        bool Cancelled = false;
-        
-        virtual void Cancel() override
-        {
-            Cancelled = true;
-            if(Running)
-            {
-                Running = false;
-                dispatch_async(dispatch_get_main_queue(), ^{
-                    [[NSApplication sharedApplication] stop:nil];
-                    NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
-                                                        location:NSMakePoint(0, 0)
-                                                   modifierFlags:0
-                                                       timestamp:0
-                                                    windowNumber:0
-                                                         context:nil
-                                                         subtype:0
-                                                           data1:0
-                                                           data2:0];
-                    [NSApp postEvent:event atStart:YES];
-                });
-            }
-        }
-
-    };
-    
+    CFRunLoopObserverRef _observer = nil;
 public:
     FORWARD_IUNKNOWN()
-    ComPtr<IAvnSignaledCallback> SignaledCallback;
-
     PlatformThreadingInterface()
     {
         _signaler = [Signaler new];
-        [_signaler setParent:this];
-    }
+    };
     
     ~PlatformThreadingInterface()
     {
-        if(_signaler)
-            [_signaler setParent: NULL];
-        _signaler = NULL;
+        [_signaler destroyObserver];
     }
     
-    virtual bool GetCurrentThreadIsLoopThread() override
+    bool GetCurrentThreadIsLoopThread() override
     {
         return [NSThread isMainThread];
-    }
-    virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override
+    };
+    
+    
+    
+    void SetEvents(IAvnPlatformThreadingInterfaceEvents *cb) override
     {
-        SignaledCallback = cb;
-    }
-    virtual IAvnLoopCancellation* CreateLoopCancellation() override
+        _events = cb;
+        [_signaler setEvents:cb];
+    };
+    
+    IAvnLoopCancellation *CreateLoopCancellation() override
     {
         return new LoopCancellation();
-    }
+    };
     
-    virtual HRESULT RunLoop(IAvnLoopCancellation* cancel) override
+    void RunLoop(IAvnLoopCancellation *cancel) override
     {
         START_COM_CALL;
-        
         auto can = dynamic_cast<LoopCancellation*>(cancel);
         if(can->Cancelled)
-            return S_OK;
-        if(_wasRunningAtLeastOnce)
-            return E_FAIL;
+            return;
         can->Running = true;
-        _wasRunningAtLeastOnce = true;
-        [NSApp run];
-        return S_OK;
-    }
+        if(![NSApp isRunning])
+        {
+            can->IsApp = true;
+            [NSApp run];
+            return;
+        }
+        else
+        {
+            while(!can->Cancelled)
+            {
+                @autoreleasepool
+                {
+                    NSEvent* ev = [NSApp
+                                   nextEventMatchingMask:NSEventMaskAny
+                                   untilDate: [NSDate dateWithTimeIntervalSinceNow:1]
+                                   inMode:NSDefaultRunLoopMode
+                                   dequeue:true];
+                    if(ev != NULL)
+                        [NSApp sendEvent:ev];
+                }
+            }
+        }
+    };
     
-    virtual void Signal(int priority) override
+    void Signal() override
     {
-        [_signaler signal:priority];
-    }
+        [_signaler signal];
+    };
     
-    virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) override
-    {
-        @autoreleasepool {
-            
-            return new TimerWrapper(callback, ms);
-        }
-    }
-};
-
-@implementation Signaler
-
-PlatformThreadingInterface* _parent = 0;
-bool _signaled = 0;
-NSArray<NSString*>* _modes;
-
--(Signaler*) init
-{
-    if(self = [super init])
+    void UpdateTimer(int ms) override
     {
-        _modes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
-    }
-    return self;
-}
-
--(void) perform
-{
-    ComPtr<IAvnSignaledCallback> cb;
-    @synchronized (self) {
-        _signaled  = false;
-        if(_parent != NULL)
-            cb = _parent->SignaledCallback;
-    }
-    if(cb != nullptr)
-        cb->Signaled(0, false);
-}
-
--(void) setParent:(PlatformThreadingInterface *)parent
-{
-    @synchronized (self) {
-        _parent = parent;
-    }
-}
-
--(void) signal: (int) priority
-{
-
-    @synchronized (self) {
-        if(_signaled)
-            return;
-        _signaled = true;
-        [self performSelector:@selector(perform) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes:_modes];
+        [_signaler updateTimer:ms];
+    };
+    
+    void RequestBackgroundProcessing() override {
+        [_signaler requestBackgroundProcessing];
     }
     
-}
-@end
-
+    
+};
 
 extern IAvnPlatformThreadingInterface* CreatePlatformThreading()
 {

+ 9 - 5
nukebuild/Build.cs

@@ -221,16 +221,18 @@ partial class Build : NukeBuild
         .Executes(() =>
         {
             RunCoreTest("Avalonia.Skia.RenderTests");
-            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            if (Parameters.IsRunningOnWindows)
                 RunCoreTest("Avalonia.Direct2D1.RenderTests");
         });
 
-    Target RunDesignerTests => _ => _
-        .OnlyWhenStatic(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
+    Target RunToolsTests => _ => _
+        .OnlyWhenStatic(() => !Parameters.SkipTests)
         .DependsOn(Compile)
         .Executes(() =>
         {
-            RunCoreTest("Avalonia.DesignerSupport.Tests");
+            RunCoreTest("Avalonia.Generators.Tests");
+            if (Parameters.IsRunningOnWindows)
+                RunCoreTest("Avalonia.DesignerSupport.Tests");
         });
 
     Target RunLeakTests => _ => _
@@ -272,12 +274,14 @@ partial class Build : NukeBuild
             if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,
                 new NumergeNukeLogger()))
                 throw new Exception("Package merge failed");
+            RefAssemblyGenerator.GenerateRefAsmsInPackage(Parameters.NugetRoot / "Avalonia." +
+                                                          Parameters.Version + ".nupkg");
         });
 
     Target RunTests => _ => _
         .DependsOn(RunCoreLibsTests)
         .DependsOn(RunRenderTests)
-        .DependsOn(RunDesignerTests)
+        .DependsOn(RunToolsTests)
         .DependsOn(RunHtmlPreviewerTests)
         .DependsOn(RunLeakTests);
 

+ 24 - 0
nukebuild/Helpers.cs

@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+using Nuke.Common.Utilities;
+
+class Helpers
+{
+    public static IDisposable UseTempDir(out string dir)
+    {
+        var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+        Directory.CreateDirectory(path);
+        dir = path;
+        return DelegateDisposable.CreateBracket(null, () =>
+        {
+            try
+            {
+                Directory.Delete(path, true);
+            }
+            catch
+            {
+                // ignore
+            }
+        });
+    }
+}

+ 171 - 0
nukebuild/RefAssemblyGenerator.cs

@@ -0,0 +1,171 @@
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using ILRepacking;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+public class RefAssemblyGenerator
+{
+    class Resolver : DefaultAssemblyResolver, IAssemblyResolver
+    {
+        private readonly string _dir;
+        Dictionary<string, AssemblyDefinition> _cache = new();
+
+        public Resolver(string dir)
+        {
+            _dir = dir;
+        }
+
+        public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
+        {
+            if (_cache.TryGetValue(name.Name, out var asm))
+                return asm;
+            var path = Path.Combine(_dir, name.Name + ".dll");
+            if (File.Exists(path))
+                return _cache[name.Name] = AssemblyDefinition.ReadAssembly(path, parameters);
+            return base.Resolve(name, parameters);
+        }
+    }
+    
+    public static void PatchRefAssembly(string file)
+    {
+        var reader = typeof(RefAssemblyGenerator).Assembly.GetManifestResourceStream("avalonia.snk");
+        var snk = new byte[reader.Length];
+        reader.Read(snk, 0, snk.Length);
+
+        var def = AssemblyDefinition.ReadAssembly(file, new ReaderParameters
+        {
+            ReadWrite = true,
+            InMemory = true,
+            ReadSymbols = true,
+            SymbolReaderProvider = new DefaultSymbolReaderProvider(false),
+            AssemblyResolver = new Resolver(Path.GetDirectoryName(file))
+        });
+
+        var obsoleteAttribute = def.MainModule.ImportReference(new TypeReference("System", "ObsoleteAttribute", def.MainModule,
+            def.MainModule.TypeSystem.CoreLibrary));
+        var obsoleteCtor = def.MainModule.ImportReference(new MethodReference(".ctor",
+            def.MainModule.TypeSystem.Void, obsoleteAttribute)
+        {
+            Parameters = { new ParameterDefinition(def.MainModule.TypeSystem.String) }
+        });
+
+        foreach(var t in def.MainModule.Types)
+            ProcessType(t, obsoleteCtor);
+        def.Write(file, new WriterParameters()
+        {
+            StrongNameKeyBlob = snk,
+            WriteSymbols = def.MainModule.HasSymbols,
+            SymbolWriterProvider = new EmbeddedPortablePdbWriterProvider(),
+            DeterministicMvid = def.MainModule.HasSymbols
+        });
+    }
+
+    static void ProcessType(TypeDefinition type, MethodReference obsoleteCtor)
+    {
+        foreach (var nested in type.NestedTypes)
+            ProcessType(nested, obsoleteCtor);
+        if (type.IsInterface)
+        {
+            var hideMethods = type.Name.EndsWith("Impl")
+                              || (type.HasCustomAttributes && type.CustomAttributes.Any(a =>
+                                  a.AttributeType.FullName == "Avalonia.Metadata.PrivateApiAttribute"));
+
+            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)
+            {
+                type.Methods.Add(new MethodDefinition("NotClientImplementable",
+                    MethodAttributes.Assembly
+                    | MethodAttributes.Abstract
+                    | MethodAttributes.NewSlot
+                    | MethodAttributes.HideBySig, type.Module.TypeSystem.Void));
+            }
+
+            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);
+            
+        }
+    }
+
+    static void MarkAsUnstable(IMemberDefinition def, MethodReference obsoleteCtor, bool force)
+    {
+        if (!force && (
+            def.HasCustomAttributes == false
+            || def.CustomAttributes.All(a => a.AttributeType.FullName != "Avalonia.Metadata.UnstableAttribute")))
+            return;
+
+        if (def.CustomAttributes.Any(a => a.AttributeType.FullName == "System.ObsoleteAttribute"))
+            return;
+
+        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")
+            }
+        });
+    }
+    
+    public static void GenerateRefAsmsInPackage(string packagePath)
+    {
+        using (var archive = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.ReadWrite),
+            ZipArchiveMode.Update))
+        {
+            foreach (var entry in archive.Entries.ToList())
+            {
+                if (entry.FullName.StartsWith("ref/"))
+                    entry.Delete();
+            }
+            
+            foreach (var entry in archive.Entries.ToList())
+            {
+                if (entry.FullName.StartsWith("lib/") && entry.Name.EndsWith(".xml"))
+                {
+                    var newEntry = archive.CreateEntry("ref/" + entry.FullName.Substring(4),
+                        CompressionLevel.Optimal);
+                    using (var src = entry.Open())
+                    using (var dst = newEntry.Open())
+                        src.CopyTo(dst);
+                }
+            }
+
+            var libs = archive.Entries.Where(e => e.FullName.StartsWith("lib/") && e.FullName.EndsWith(".dll"))
+                .Select((e => new { s = e.FullName.Split('/'), e = e }))
+                .Select(e => new { Tfm = e.s[1], Name = e.s[2], Entry = e.e })
+                .GroupBy(x => x.Tfm);
+            foreach(var tfm in libs)
+                using (Helpers.UseTempDir(out var temp))
+                {
+                    foreach (var l in tfm) 
+                        l.Entry.ExtractToFile(Path.Combine(temp, l.Name));
+                    foreach (var l in tfm) 
+                        PatchRefAssembly(Path.Combine(temp, l.Name));
+                    foreach (var l in tfm)
+                        archive.CreateEntryFromFile(Path.Combine(temp, l.Name), $"ref/{l.Tfm}/{l.Name}");
+                }
+        }
+    }
+}

+ 3 - 10
nukebuild/_build.csproj

@@ -31,18 +31,11 @@
     <!-- Common build related files -->
     <Compile Remove="Numerge/**/*.*" />
     <Compile Include="Numerge/Numerge/**/*.cs" />
-  </ItemGroup>
-
-  	<ItemGroup>
-		<EmbeddedResource Include="$(NuGetPackageRoot)sourcelink/1.1.0/tools/pdbstr.exe"></EmbeddedResource>
-	</ItemGroup>
-    
-  <ItemGroup>
+	<EmbeddedResource Include="$(NuGetPackageRoot)sourcelink/1.1.0/tools/pdbstr.exe"></EmbeddedResource>
+	<EmbeddedResource Include="../build/avalonia.snk"></EmbeddedResource>
     <Compile Remove="il-repack\ILRepack\Application.cs" />
   </ItemGroup>
     
-  <ItemGroup>
-    <Folder Include="Numerge\Numerge.Console\" />
-  </ItemGroup>
+    
 
 </Project>

+ 5 - 0
nukebuild/numerge.config

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

+ 9 - 1
packages/Avalonia/Avalonia.csproj

@@ -6,11 +6,15 @@
 
   <ItemGroup>
       <ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" />
-      <ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" >
+      <ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj">
         <PrivateAssets>all</PrivateAssets>
         <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties>
         <SetTargetFramework>TargetFramework=netstandard2.0</SetTargetFramework>
       </ProjectReference>
+      <ProjectReference Include="..\..\src\tools\Avalonia.Generators\Avalonia.Generators.csproj"
+                        ReferenceOutputAssembly="false"
+                        PrivateAssets="all"
+                        OutputItemType="Analyzer" />
   </ItemGroup>
 
   <PropertyGroup>
@@ -39,6 +43,10 @@
        <Pack>true</Pack>
        <PackagePath>build\;buildTransitive\</PackagePath>
     </Content>
+    <Content Include="..\..\src\tools\Avalonia.Generators\Avalonia.Generators.props">
+      <Pack>true</Pack>
+      <PackagePath>build\;buildTransitive\</PackagePath>
+    </Content>
     <Content Include="*.targets">
       <Pack>true</Pack>
       <PackagePath>build\;buildTransitive\</PackagePath>

+ 1 - 0
packages/Avalonia/Avalonia.props

@@ -6,6 +6,7 @@
     <AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild>
   </PropertyGroup>
   <Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/>
+  <Import Project="$(MSBuildThisFileDirectory)\Avalonia.Generators.props"/>
 
   <!-- Allow loading the AvaloniaVS extension when referencing the Avalonia nuget package -->
   <ItemGroup>

+ 4 - 0
readme.md

@@ -5,6 +5,10 @@
 <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.
+
 ## 📖 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.

+ 2 - 2
samples/BindingDemo/MainWindow.xaml

@@ -75,11 +75,11 @@
         </StackPanel.DataTemplates>
         <StackPanel Margin="18" Spacing="4" Width="200">
           <TextBlock FontSize="16" Text="Multiple"/>
-          <ListBox Items="{Binding Items}" SelectionMode="Multiple" Selection="{Binding Selection}"/>
+          <ListBox ItemsSource="{Binding Items}" SelectionMode="Multiple" Selection="{Binding Selection}"/>
         </StackPanel>
         <StackPanel Margin="18" Spacing="4" Width="200">
           <TextBlock FontSize="16" Text="Multiple"/>
-          <ListBox Items="{Binding Items}" SelectionMode="Multiple" Selection="{Binding Selection}"/>
+          <ListBox ItemsSource="{Binding Items}" SelectionMode="Multiple" Selection="{Binding Selection}"/>
         </StackPanel>
         <ContentControl Content="{ReflectionBinding Selection.SelectedItems[0]}">
           <ContentControl.DataTemplates>

+ 1 - 1
samples/ControlCatalog.Android/MainActivity.cs

@@ -5,7 +5,7 @@ using Avalonia.Android;
 
 namespace ControlCatalog.Android
 {
-    [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.Main", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+    [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.Main", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
     public class MainActivity : AvaloniaMainActivity
     {
     }

+ 2 - 0
samples/ControlCatalog.Android/Properties/AndroidManifest.xml

@@ -2,4 +2,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
   <application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application>
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE " />
 </manifest>

+ 4 - 0
samples/ControlCatalog.Android/Resources/values-night/colors.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <color name="splash_background">#212121</color>
+</resources>

+ 3 - 11
samples/ControlCatalog/ControlCatalog.csproj

@@ -2,7 +2,8 @@
   <PropertyGroup>
     <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-    <Nullable>enable</Nullable>    
+    <Nullable>enable</Nullable>
+    <IncludeAvaloniaGenerators>true</IncludeAvaloniaGenerators>
   </PropertyGroup>
   <ItemGroup>
     <Compile Update="**\*.xaml.cs">
@@ -35,14 +36,5 @@
   </ItemGroup>
 
   <Import Project="..\..\build\BuildTargets.targets" />
-
-  <ItemGroup>
-    <None Remove="Pages\CustomDrawing.xaml" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <AvaloniaResource Update="Pages\CustomDrawing.xaml">
-      <Generator></Generator>
-    </AvaloniaResource>
-  </ItemGroup>
+  <Import Project="..\..\build\SourceGenerators.props" />
 </Project>

+ 1 - 4
samples/ControlCatalog/MainView.xaml

@@ -147,9 +147,6 @@
       <TabItem Header="ScrollViewer">
         <pages:ScrollViewerPage />
       </TabItem>
-      <TabItem Header="ScrollViewer Snapping">
-        <pages:ScrollSnapPage />
-      </TabItem>
       <TabItem Header="Slider">
         <pages:SliderPage />
       </TabItem>
@@ -241,7 +238,7 @@
               </ComboBox.Items>
             </ComboBox>
             <ComboBox HorizontalAlignment="Stretch"
-                      Items="{Binding WindowStates}"
+                      ItemsSource="{Binding WindowStates}"
                       SelectedItem="{Binding WindowState}" />
           </StackPanel>
         </Flyout>

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

@@ -114,7 +114,7 @@ namespace ControlCatalog.Pages
 
             foreach (AutoCompleteBox box in GetAllAutoCompleteBox().Where(x => x.Name != "CustomAutocompleteBox"))
             {
-                box.Items = States;
+                box.ItemsSource = States;
             }
 
             var converter = new FuncMultiValueConverter<string, string>(parts =>
@@ -132,7 +132,7 @@ namespace ControlCatalog.Pages
             asyncBox.AsyncPopulator = PopulateAsync;
 
             var customAutocompleteBox = this.Get<AutoCompleteBox>("CustomAutocompleteBox");
-            customAutocompleteBox.Items = Sentences.SelectMany(x => x);
+            customAutocompleteBox.ItemsSource = Sentences.SelectMany(x => x);
             customAutocompleteBox.TextFilter = LastWordContains;
             customAutocompleteBox.TextSelector = AppendWord;
         }

+ 56 - 18
samples/ControlCatalog/Pages/ClipboardPage.xaml.cs

@@ -1,16 +1,23 @@
 using System;
 using System.Collections.Generic;
-
+using System.Linq;
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Controls.Notifications;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Markup.Xaml;
+using Avalonia.Platform;
+using Avalonia.Platform.Storage;
+using Avalonia.Platform.Storage.FileIO;
 
 namespace ControlCatalog.Pages
 {
     public partial class ClipboardPage : UserControl
     {
+        private INotificationManager? _notificationManager;
+        private INotificationManager NotificationManager => _notificationManager
+            ??= new WindowNotificationManager(TopLevel.GetTopLevel(this)!);
         public ClipboardPage()
         {
             InitializeComponent();
@@ -25,13 +32,13 @@ namespace ControlCatalog.Pages
 
         private async void CopyText(object? sender, RoutedEventArgs args)
         {
-            if (Application.Current!.Clipboard is { } clipboard && ClipboardContent is { } clipboardContent)
+            if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard && ClipboardContent is { } clipboardContent)
                 await clipboard.SetTextAsync(clipboardContent.Text ?? String.Empty);
         }
 
         private async void PasteText(object? sender, RoutedEventArgs args)
         {
-            if(Application.Current!.Clipboard is { } clipboard)
+            if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
             {
                 ClipboardContent.Text = await clipboard.GetTextAsync();
             }
@@ -39,7 +46,7 @@ namespace ControlCatalog.Pages
 
         private async void CopyTextDataObject(object? sender, RoutedEventArgs args)
         {
-            if (Application.Current!.Clipboard is { } clipboard)
+            if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
             {
                 var dataObject = new DataObject();
                 dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty);
@@ -49,7 +56,7 @@ namespace ControlCatalog.Pages
 
         private async void PasteTextDataObject(object? sender, RoutedEventArgs args)
         {
-            if (Application.Current!.Clipboard is { } clipboard)
+            if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
             {
                 ClipboardContent.Text = await clipboard.GetDataAsync(DataFormats.Text) as string ?? string.Empty;
             }
@@ -57,32 +64,63 @@ namespace ControlCatalog.Pages
 
         private async void CopyFilesDataObject(object? sender, RoutedEventArgs args)
         {
-            if (Application.Current!.Clipboard is { } clipboard)
+            if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
             {
-                var files = (ClipboardContent.Text ?? String.Empty)
-                .Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
-                if (files.Length == 0)
+                var storageProvider = TopLevel.GetTopLevel(this)!.StorageProvider;
+                var filesPath = (ClipboardContent.Text ?? string.Empty)
+                    .Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
+                if (filesPath.Length == 0)
                 {
                     return;
                 }
-                var dataObject = new DataObject();
-                dataObject.Set(DataFormats.FileNames, files);
-                await clipboard.SetDataObjectAsync(dataObject);
+                List<string> invalidFile = new(filesPath.Length);
+                List<IStorageFile> files = new(filesPath.Length);
+
+                for (int i = 0; i < filesPath.Length; i++)
+                {
+                    var file = await storageProvider.TryGetFileFromPathAsync(filesPath[i]);
+                    if (file is null)
+                    {
+                        invalidFile.Add(filesPath[i]);
+                    }
+                    else
+                    {
+                        files.Add(file);
+                    }
+                }
+
+                if (invalidFile.Count > 0)
+                {
+                    NotificationManager.Show(new Notification("Warning", "There is one o more invalid path.", NotificationType.Warning));
+                }
+
+                if (files.Count > 0)
+                {
+                    var dataObject = new DataObject();
+                    dataObject.Set(DataFormats.Files, files);
+                    await clipboard.SetDataObjectAsync(dataObject);
+                    NotificationManager.Show(new Notification("Success", "Copy completated.", NotificationType.Success));
+                }
+                else
+                {
+                    NotificationManager.Show(new Notification("Warning", "Any files to copy in Clipboard.", NotificationType.Warning));
+                }
             }
         }
 
         private async void PasteFilesDataObject(object? sender, RoutedEventArgs args)
         {
-            if (Application.Current!.Clipboard is { } clipboard)
+            if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
             {
-                var fiels = await clipboard.GetDataAsync(DataFormats.FileNames) as IEnumerable<string>;
-                ClipboardContent.Text = fiels != null ? string.Join(Environment.NewLine, fiels) : string.Empty;
+                var files = await clipboard.GetDataAsync(DataFormats.Files) as IEnumerable<Avalonia.Platform.Storage.IStorageItem>;
+
+                ClipboardContent.Text = files != null ? string.Join(Environment.NewLine, files.Select(f => f.TryGetLocalPath() ?? f.Name)) : string.Empty;
             }
         }
 
         private async void GetFormats(object sender, RoutedEventArgs args)
         {
-            if (Application.Current!.Clipboard is { } clipboard)
+            if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
             {
                 var formats = await clipboard.GetFormatsAsync();
                 ClipboardContent.Text = string.Join(Environment.NewLine, formats);
@@ -91,11 +129,11 @@ namespace ControlCatalog.Pages
 
         private async void Clear(object sender, RoutedEventArgs args)
         {
-            if (Application.Current!.Clipboard is { } clipboard)
+            if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
             {
                 await clipboard.ClearAsync();
             }
-                
+
         }
     }
 }

+ 35 - 0
samples/ControlCatalog/Pages/ColorPickerPage.xaml

@@ -28,6 +28,41 @@
     <Grid Grid.Column="2"
           Grid.Row="0"
           RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
+      <Grid.Resources>
+
+        <x:Double x:Key="ColorSliderSize">24</x:Double>
+        <x:Double x:Key="ColorSliderTrackSize">18</x:Double>
+        <CornerRadius x:Key="ColorSliderCornerRadius">12</CornerRadius>
+        <CornerRadius x:Key="ColorSliderTrackCornerRadius">9</CornerRadius>
+
+        <!-- Due to 'SystemControlForegroundBaseHighBrush' usage this only works in Fluent theme. -->
+        <!-- Otherwise it would be necessary to make custom light/dark resources. -->
+        <ControlTheme x:Key="ColorSliderThumbTheme"
+                      TargetType="Thumb">
+          <Setter Property="Background" Value="Transparent" />
+          <Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
+          <Setter Property="BorderThickness" Value="5" />
+          <Setter Property="CornerRadius" Value="{DynamicResource ColorSliderCornerRadius}" />
+          <Setter Property="Template">
+            <Setter.Value>
+              <ControlTemplate>
+                <Grid>
+                  <Border Background="{TemplateBinding Background}"
+                          BorderBrush="{TemplateBinding BorderBrush}"
+                          BorderThickness="{TemplateBinding BorderThickness}"
+                          CornerRadius="{TemplateBinding CornerRadius}" />
+                  <Ellipse Height="{TemplateBinding Height}"
+                           Width="{TemplateBinding Width}"
+                           Fill="Transparent"
+                           Stroke="{TemplateBinding Foreground}"
+                           StrokeThickness="1" />
+                </Grid>
+              </ControlTemplate>
+            </Setter.Value>
+          </Setter>
+        </ControlTheme>
+
+      </Grid.Resources>
       <ColorSpectrum x:Name="ColorSpectrum1"
                      Grid.Row="0"
                      Color="Red"

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

@@ -18,7 +18,7 @@ namespace ControlCatalog.Pages
         {
             AvaloniaXamlLoader.Load(this);
             var fontComboBox = this.Get<ComboBox>("fontComboBox");
-            fontComboBox.Items = FontManager.Current.SystemFonts;
+            fontComboBox.ItemsSource = FontManager.Current.SystemFonts;
             fontComboBox.SelectedIndex = 0;
         }
     }

+ 1 - 1
samples/ControlCatalog/Pages/CompositionPage.axaml.cs

@@ -32,7 +32,7 @@ public partial class CompositionPage : UserControl
     protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
     {
         base.OnAttachedToVisualTree(e);
-        this.Get<ItemsControl>("Items").Items = CreateColorItems();
+        this.Get<ItemsControl>("Items").ItemsSource = CreateColorItems();
 
     }
 

+ 2 - 2
samples/ControlCatalog/Pages/ContextFlyoutPage.xaml

@@ -61,13 +61,13 @@
         <Border.Styles>
           <Style Selector="MenuFlyoutPresenter MenuItem" x:DataType="viewModels:MenuItemViewModel">
             <Setter Property="Header" Value="{Binding Header}"/>
-            <Setter Property="Items" Value="{Binding Items}"/>
+            <Setter Property="ItemsSource" Value="{Binding Items}"/>
             <Setter Property="Command" Value="{Binding Command}"/>
             <Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
           </Style>
         </Border.Styles>
         <Border.ContextFlyout>
-          <MenuFlyout Items="{Binding MenuItems}" />
+          <MenuFlyout ItemsSource="{Binding MenuItems}" />
         </Border.ContextFlyout>
         <TextBlock Text="Dynamically Generated"/>
       </Border>

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

@@ -36,8 +36,9 @@ namespace ControlCatalog.Pages
             customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
 
             var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
-            cancellableContextBorder.ContextFlyout!.Closing += ContextFlyoutPage_Closing;
-            cancellableContextBorder.ContextFlyout!.Opening += ContextFlyoutPage_Opening;
+            var flyout = (Flyout)cancellableContextBorder.ContextFlyout!;
+            flyout.Closing += ContextFlyoutPage_Closing;
+            flyout.Opening += ContextFlyoutPage_Opening;
         }
 
         private ContextPageViewModel? _model;

+ 2 - 2
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@@ -51,13 +51,13 @@
         <Border.Styles>
           <Style Selector="ContextMenu MenuItem" x:DataType="viewModels:MenuItemViewModel">
             <Setter Property="Header" Value="{Binding Header}"/>
-            <Setter Property="Items" Value="{Binding Items}"/>
+            <Setter Property="ItemsSource" Value="{Binding Items}"/>
             <Setter Property="Command" Value="{Binding Command}"/>
             <Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
           </Style>
         </Border.Styles>
         <Border.ContextMenu>
-          <ContextMenu Items="{Binding MenuItems}" />
+          <ContextMenu ItemsSource="{Binding MenuItems}" />
         </Border.ContextMenu>
         <TextBlock Text="Dynamically Generated"/>
       </Border>

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

@@ -8,7 +8,7 @@
       <TextBlock Classes="h2">Defines a cursor (mouse pointer)</TextBlock>
     </StackPanel>
 
-    <ListBox Grid.Row="1" Items="{Binding StandardCursors}" Margin="0 8 8 8">
+    <ListBox Grid.Row="1" ItemsSource="{Binding StandardCursors}" Margin="0 8 8 8">
       <ListBox.Styles>
         <Style Selector="ListBoxItem">
           <Setter Property="Cursor" Value="{Binding Cursor}" x:DataType="viewModels:StandardCursorModel"/>

+ 4 - 4
samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs

@@ -133,17 +133,17 @@ namespace ControlCatalog.Pages
 
             // 0,0 refers to the top-left of the control now. It is not prime time to draw gui stuff because it'll be under the world 
 
-            var translateModifier = context.PushPreTransform(Avalonia.Matrix.CreateTranslation(new Avalonia.Vector(halfWidth, halfHeight)));
+            var translateModifier = context.PushTransform(Avalonia.Matrix.CreateTranslation(new Avalonia.Vector(halfWidth, halfHeight)));
 
             // now 0,0 refers to the ViewportCenter(X,Y). 
             var rotationMatrix = Avalonia.Matrix.CreateRotation(Rotation);
-            var rotationModifier = context.PushPreTransform(rotationMatrix);
+            var rotationModifier = context.PushTransform(rotationMatrix);
 
             // everything is rotated but not scaled 
 
-            var scaleModifier = context.PushPreTransform(Avalonia.Matrix.CreateScale(Scale, -Scale));
+            var scaleModifier = context.PushTransform(Avalonia.Matrix.CreateScale(Scale, -Scale));
 
-            var mapPositionModifier = context.PushPreTransform(Matrix.CreateTranslation(new Vector(-ViewportCenterX, -ViewportCenterY)));
+            var mapPositionModifier = context.PushTransform(Matrix.CreateTranslation(new Vector(-ViewportCenterX, -ViewportCenterY)));
 
             // now everything is rotated and scaled, and at the right position, now we're drawing strictly in world coordinates
 

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

@@ -94,7 +94,7 @@
         <Grid RowDefinitions="*,Auto">
           <!-- Example of columns inheriting the data type from the Items source -->
           <DataGrid Name="dataGridEdit" Margin="12" Grid.Row="0"
-                    Items="{Binding DataGrid3Source}">
+                    ItemsSource="{Binding DataGrid3Source}">
             <DataGrid.Columns>
               <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" Width="2*" FontSize="{Binding #FontSizeSlider.Value, Mode=OneWay}" />
               <DataGridTextColumn Header="Last" Binding="{Binding LastName}" Width="2*" FontSize="{Binding #FontSizeSlider.Value, Mode=OneWay}" />

+ 2 - 2
samples/ControlCatalog/Pages/DataGridPage.xaml.cs

@@ -36,7 +36,7 @@ namespace ControlCatalog.Pages
                     collectionView1.SortDescriptions.Add(dataGridSortDescription);
                 }
             };
-            dg1.Items = collectionView1;
+            dg1.ItemsSource = collectionView1;
 
             var dg2 = this.Get<DataGrid>("dataGridGrouping");
             dg2.IsReadOnly = true;
@@ -44,7 +44,7 @@ namespace ControlCatalog.Pages
             var collectionView2 = new DataGridCollectionView(Countries.All);
             collectionView2.GroupDescriptions.Add(new DataGridPathGroupDescription("Region"));
 
-            dg2.Items = collectionView2;
+            dg2.ItemsSource = collectionView2;
 
             var dg3 = this.Get<DataGrid>("dataGridEdit");
             dg3.IsReadOnly = false;

+ 2 - 2
samples/ControlCatalog/Pages/DialogsPage.xaml

@@ -45,7 +45,7 @@
     </Expander>
 
     <AutoCompleteBox x:Name="CurrentFolderBox" Watermark="Write full path/uri or well known folder name">
-      <AutoCompleteBox.Items>
+      <AutoCompleteBox.ItemsSource>
         <generic:List x:TypeArguments="storage:WellKnownFolder">
           <storage:WellKnownFolder>Desktop</storage:WellKnownFolder>
           <storage:WellKnownFolder>Documents</storage:WellKnownFolder>
@@ -54,7 +54,7 @@
           <storage:WellKnownFolder>Videos</storage:WellKnownFolder>
           <storage:WellKnownFolder>Music</storage:WellKnownFolder>
         </generic:List>
-      </AutoCompleteBox.Items>
+      </AutoCompleteBox.ItemsSource>
     </AutoCompleteBox>
     
     <TextBlock x:Name="PickerLastResultsVisible"

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

@@ -106,7 +106,7 @@ namespace ControlCatalog.Pages
                     Directory = initialDirectory,
                     InitialFileName = initialFileName
                 }.ShowAsync(GetWindow());
-                results.Items = result;
+                results.ItemsSource = result;
                 resultsVisible.IsVisible = result?.Any() == true;
             };
             this.Get<Button>("OpenMultipleFiles").Click += async delegate
@@ -118,7 +118,7 @@ namespace ControlCatalog.Pages
                     Directory = lastSelectedDirectory?.Path is {IsAbsoluteUri:true} path ? path.LocalPath : null,
                     AllowMultiple = true
                 }.ShowAsync(GetWindow());
-                results.Items = result;
+                results.ItemsSource = result;
                 resultsVisible.IsVisible = result?.Any() == true;
             };
             this.Get<Button>("SaveFile").Click += async delegate
@@ -132,7 +132,7 @@ namespace ControlCatalog.Pages
                     DefaultExtension = filters?.Any() == true ? "txt" : null,
                     InitialFileName = "test.txt"
                 }.ShowAsync(GetWindow());
-                results.Items = new[] { result };
+                results.ItemsSource = new[] { result };
                 resultsVisible.IsVisible = result != null;
             };
             this.Get<Button>("SelectFolder").Click += async delegate
@@ -149,7 +149,7 @@ namespace ControlCatalog.Pages
                 else
                 {
                     SetFolder(await GetStorageProvider().TryGetFolderFromPathAsync(result));
-                    results.Items = new[] { result };
+                    results.ItemsSource = new[] { result };
                     resultsVisible.IsVisible = true;
                 }
             };
@@ -164,7 +164,7 @@ namespace ControlCatalog.Pages
                 {
                     AllowDirectorySelection = true
                 });
-                results.Items = result;
+                results.ItemsSource = result;
                 resultsVisible.IsVisible = result?.Any() == true;
             };
             this.Get<Button>("DecoratedWindow").Click += delegate
@@ -324,15 +324,15 @@ namespace ControlCatalog.Pages
                         mappedResults.Add("+> " + FullPathOrName(selectedItem));
                         if (selectedItem is IStorageFolder folder)
                         {
-                            foreach (var innerItems in await folder.GetItemsAsync())
+                            await foreach (var innerItem in folder.GetItemsAsync())
                             {
-                                mappedResults.Add("++> " + FullPathOrName(innerItems));
+                                mappedResults.Add("++> " + FullPathOrName(innerItem));
                             }
                         }
                     }
                 }
 
-                results.Items = mappedResults;
+                results.ItemsSource = mappedResults;
                 resultsVisible.IsVisible = mappedResults.Any();
             }
         }

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

@@ -104,8 +104,12 @@ namespace ControlCatalog.Pages
                         }
                         else if (item is IStorageFolder folder)
                         {
-                            var items = await folder.GetItemsAsync();
-                            contentStr += $"Folder {item.Name}: items {items.Count}{Environment.NewLine}{Environment.NewLine}";
+                            var childrenCount = 0;
+                            await foreach (var _ in folder.GetItemsAsync())
+                            {
+                                childrenCount++;
+                            }
+                            contentStr += $"Folder {item.Name}: items {childrenCount}{Environment.NewLine}{Environment.NewLine}";
                         }
                     }
 

+ 16 - 2
samples/ControlCatalog/Pages/FlyoutsPage.axaml

@@ -16,8 +16,13 @@
             <MenuItem Header="Item 3" />
         </MenuFlyout>
         <Flyout Placement="Bottom" x:Key="BasicFlyout">
+            <Flyout.FlyoutPresenterTheme>
+                <ControlTheme TargetType="FlyoutPresenter" BasedOn="{StaticResource {x:Type FlyoutPresenter}}">
+                    <Setter Property="CornerRadius" Value="20" />
+                </ControlTheme>
+            </Flyout.FlyoutPresenterTheme>
             <Panel Width="100" Height="100">
-                <TextBlock Text="Flyout Content!" />
+                <TextBlock Text="Flyout Content with a custom presenter theme!" TextWrapping="Wrap" />
             </Panel>
         </Flyout>
     </UserControl.Resources>
@@ -136,6 +141,15 @@
                                 </Flyout>
                             </Button.Flyout>
                         </Button>
+                        <Button Content="Placement=Center">
+                            <Button.Flyout>
+                                <Flyout Placement="Center">
+                                    <Panel Width="100" Height="100">
+                                        <TextBlock Text="Flyout Content!" />
+                                    </Panel>
+                                </Flyout>
+                            </Button.Flyout>
+                        </Button>
                         <Button Content="Placement=TopEdgeAlignedLeft">
                             <Button.Flyout>
                                 <Flyout Placement="TopEdgeAlignedLeft">
@@ -190,7 +204,7 @@
                                 </Flyout>
                             </Button.Flyout>
                         </Button>
-                        <Button Content="Placement=RightEdgeAlignedBottom">
+                        <Button Content="Placement=RightEdgeAlignedTop">
                             <Button.Flyout>
                                 <Flyout Placement="RightEdgeAlignedTop">
                                     <Panel Width="100" Height="100">

+ 1 - 7
samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs

@@ -1,11 +1,10 @@
 using Avalonia.Controls;
 using Avalonia.Controls.Primitives;
-using Avalonia.Markup.Xaml;
 using Avalonia.Interactivity;
 
 namespace ControlCatalog.Pages
 {
-    public class FlyoutsPage : UserControl
+    public partial class FlyoutsPage : UserControl
     {
         public FlyoutsPage()
         {
@@ -28,11 +27,6 @@ namespace ControlCatalog.Pages
             }
         }
 
-        private void InitializeComponent()
-        {
-            AvaloniaXamlLoader.Load(this);
-        }
-
         private void SetXamlTexts()
         {
             var bfxt = this.Get<TextBlock>("ButtonFlyoutXamlText");

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

@@ -66,7 +66,7 @@
       <ScrollViewer Name="scroller"
                     HorizontalScrollBarVisibility="Auto"
                     VerticalScrollBarVisibility="Auto">
-        <ItemsRepeater Name="repeater" Background="Transparent" Items="{Binding Items}"
+        <ItemsRepeater Name="repeater" Background="Transparent" ItemsSource="{Binding Items}"
                        ItemTemplate="{StaticResource elementFactory}"/>
       </ScrollViewer>
     </Border>

+ 2 - 9
samples/ControlCatalog/Pages/LabelsPage.axaml.cs

@@ -1,11 +1,9 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
+using Avalonia.Controls;
 using ControlCatalog.Models;
 
 namespace ControlCatalog.Pages
 {
-    public class LabelsPage : UserControl
+    public partial class LabelsPage : UserControl
     {
         private Person? _person;
 
@@ -25,11 +23,6 @@ namespace ControlCatalog.Pages
             };
         }
 
-        private void InitializeComponent()
-        {
-            AvaloniaXamlLoader.Load(this);
-        }
-
         public void DoSave()
         {
             

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

@@ -30,7 +30,7 @@
       <Button Command="{Binding RemoveItemCommand}">Remove</Button>
       <Button Command="{Binding SelectRandomItemCommand}">Select Random Item</Button>
     </StackPanel>
-    <ListBox Items="{Binding Items}"
+    <ListBox ItemsSource="{Binding Items}"
              Selection="{Binding Selection}"
              DisplayMemberBinding="{Binding (viewModels:ItemModel).ID, StringFormat='{}Item {0:N0}'}"
              AutoScrollToSelectedItem="{Binding AutoScrollToSelectedItem}"

+ 3 - 3
samples/ControlCatalog/Pages/MenuPage.xaml

@@ -45,11 +45,11 @@
 
             <StackPanel>
                 <TextBlock Classes="h3" Margin="4 8">Dyanamically generated</TextBlock>
-                <Menu Items="{Binding MenuItems}">
+                <Menu ItemsSource="{Binding MenuItems}">
                     <Menu.Styles>
                         <Style Selector="MenuItem" x:DataType="viewModels:MenuItemViewModel">
                             <Setter Property="Header" Value="{Binding Header}"/>
-                            <Setter Property="Items" Value="{Binding Items}"/>
+                            <Setter Property="ItemsSource" Value="{Binding Items}"/>
                             <Setter Property="Command" Value="{Binding Command}"/>
                             <Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
                         </Style>
@@ -68,7 +68,7 @@
                         <Separator/>
                         <MenuItem Header="Execu_te Script..." />
                         <Separator/>
-                        <MenuItem Header="_Recent" Items="{Binding RecentItems}">
+                        <MenuItem Header="_Recent" ItemsSource="{Binding RecentItems}">
                             <MenuItem.Styles>
                                 <Style Selector="MenuItem" x:DataType="viewModels:MenuItemViewModel">
                                     <Setter Property="Header" Value="{Binding Header}"/>

+ 4 - 4
samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs

@@ -33,10 +33,10 @@ namespace ControlCatalog.Pages
         {
             new ContextMenu()
             {
-                Items = new List<MenuItem>
-            {
-                new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" }
-            }
+                Items =
+                {
+                    new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" }
+                }
             }.Open((Control)sender);
         }
 

+ 3 - 3
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@@ -27,7 +27,7 @@
       </Grid>
       <Grid Grid.Row="0" Grid.Column="1" Margin="8" ColumnDefinitions="Auto, 120" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
         <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="2">FormatString:</TextBlock>
-        <ComboBox Grid.Row="0" Grid.Column="1" Items="{Binding Formats}" SelectedItem="{Binding SelectedFormat}"
+        <ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding Formats}" SelectedItem="{Binding SelectedFormat}"
                   VerticalAlignment="Center" Margin="2">
           <ComboBox.ItemTemplate>
             <DataTemplate>
@@ -41,11 +41,11 @@
         </ComboBox>
 
         <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="2">ButtonSpinnerLocation:</TextBlock>
-        <ComboBox Grid.Row="1" Grid.Column="1" Items="{Binding SpinnerLocations}" SelectedItem="{Binding #upDown.ButtonSpinnerLocation}"
+        <ComboBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding SpinnerLocations}" SelectedItem="{Binding #upDown.ButtonSpinnerLocation}"
                   VerticalAlignment="Center" Margin="2"/>
 
         <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="2">CultureInfo:</TextBlock>
-        <ComboBox x:Name="CultureSelector" Grid.Row="2" Grid.Column="1" Items="{Binding Cultures}"
+        <ComboBox x:Name="CultureSelector" Grid.Row="2" Grid.Column="1" ItemsSource="{Binding Cultures}"
                   VerticalAlignment="Center" Margin="2"/>
 
         <TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="2">Watermark:</TextBlock>

+ 1 - 1
samples/ControlCatalog/Pages/RefreshContainerPage.axaml

@@ -21,7 +21,7 @@
                       Margin="5">
       <ListBox HorizontalAlignment="Stretch"
                VerticalAlignment="Top"
-               Items="{Binding Items}"/>
+               ItemsSource="{Binding Items}"/>
     </RefreshContainer>
   </DockPanel>
 </UserControl>

+ 3 - 11
samples/ControlCatalog/Pages/RefreshContainerPage.axaml.cs

@@ -1,18 +1,15 @@
-using System.Threading.Tasks;
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
+using Avalonia.Controls;
 using ControlCatalog.ViewModels;
 
 namespace ControlCatalog.Pages
 {
-    public class RefreshContainerPage : UserControl
+    public partial class RefreshContainerPage : UserControl
     {
         private RefreshContainerViewModel _viewModel;
 
         public RefreshContainerPage()
         {
-            this.InitializeComponent();
+            InitializeComponent();
 
             _viewModel = new RefreshContainerViewModel();
 
@@ -27,10 +24,5 @@ namespace ControlCatalog.Pages
 
             deferral.Complete();
         }
-
-        private void InitializeComponent()
-        {
-            AvaloniaXamlLoader.Load(this);
-        }
     }
 }

+ 3 - 10
samples/ControlCatalog/Pages/RelativePanelPage.axaml.cs

@@ -1,19 +1,12 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
+using Avalonia.Controls;
 
 namespace ControlCatalog.Pages
 {
-    public class RelativePanelPage : UserControl
+    public partial class RelativePanelPage : UserControl
     {
         public RelativePanelPage()
         {
-            this.InitializeComponent();
-        }
-
-        private void InitializeComponent()
-        {
-            AvaloniaXamlLoader.Load(this);
+            InitializeComponent();
         }
     }
 }

+ 0 - 222
samples/ControlCatalog/Pages/ScrollSnapPage.xaml

@@ -1,222 +0,0 @@
-<UserControl xmlns="https://github.com/avaloniaui"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-             d:DesignHeight="800"
-             d:DesignWidth="400"
-             x:Class="ControlCatalog.Pages.ScrollSnapPage"
-             xmlns:pages="using:ControlCatalog.Pages"
-             x:DataType="pages:ScrollSnapPageViewModel">
-  <StackPanel Orientation="Vertical" Spacing="4">
-    <TextBlock TextWrapping="Wrap"
-               Classes="h2">Scrollviewer can snap supported content both vertically and horizontally. Snapping occurs from scrolling with touch or pen.</TextBlock>
-
-    <Grid RowDefinitions="Auto, Auto, Auto, Auto, Auto">
-      <StackPanel Orientation="Horizontal"
-                  Spacing="4">
-        <StackPanel Orientation="Vertical"
-                    Spacing="4">
-          <TextBlock Text="Snap Point Type" />
-          <ComboBox Items="{Binding AvailableSnapPointsType}"
-                    SelectedItem="{Binding SnapPointsType}" />
-        </StackPanel>
-
-        <StackPanel Orientation="Vertical"
-                    Spacing="4">
-          <TextBlock Text="Snap Point Alignment" />
-          <ComboBox Items="{Binding AvailableSnapPointsAlignment}"
-                    SelectedItem="{Binding SnapPointsAlignment}" />
-        </StackPanel>
-
-        <ToggleSwitch IsChecked="{Binding AreSnapPointsRegular}"
-                      OffContent="No"
-                      OnContent="Yes"
-                      Content="Are Snap Points regular?" />
-      </StackPanel>
-      <TextBlock TextWrapping="Wrap"
-                 Grid.Row="1"
-                 Margin="0,10"
-                 Classes="h2">Vertical Snapping</TextBlock>
-
-      <Border
-        BorderBrush="Green"
-        BorderThickness="1"
-        Padding="0"
-        Grid.Row="2"
-        Margin="10, 5">
-        <ScrollViewer x:Name="VerticalSnapsScrollViewer"
-                      VerticalSnapPointsType="{Binding SnapPointsType}"
-                      VerticalSnapPointsAlignment="{Binding SnapPointsAlignment}"
-                      HorizontalAlignment="Stretch"
-                      Height="350"
-                      HorizontalScrollBarVisibility="Disabled">
-          <StackPanel AreVerticalSnapPointsRegular="{Binding AreSnapPointsRegular}"
-                      Orientation="Vertical"
-                      HorizontalAlignment="Stretch">
-            <Border Padding="5, 30"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 1"/>
-            </Border>
-            <Border Padding="5, 30"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 2"/>
-            </Border>
-            <Border Padding="5, 20"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 3"/>
-            </Border>
-            <Border Padding="5, 30"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 4"/>
-            </Border>
-            <Border Padding="5, 30"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 5"/>
-            </Border>
-            <Border Padding="5, 30"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 6"/>
-            </Border>
-            <Border Padding="5,8"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 7"/>
-            </Border>
-            <Border Padding="5, 30"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 8"/>
-            </Border>
-            <Border Padding="5,4"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 9"/>
-            </Border>
-            <Border Padding="5, 30"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 20"/>
-            </Border>
-            <Border Padding="5, 30"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 11"/>
-            </Border>
-            <Border Padding="5, 30"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         Text="Child 12"/>
-            </Border>
-          </StackPanel>
-        </ScrollViewer>
-      </Border>
-      <TextBlock TextWrapping="Wrap"
-                 Grid.Row="3"
-                 Margin="0,10"
-                 Classes="h2">Horizontal Snapping</TextBlock>
-      <Border
-        BorderBrush="Green"
-        BorderThickness="1"
-        Padding="0"
-        Grid.Row="4"
-        Margin="10, 10">
-        <ScrollViewer x:Name="HorizontalSnapsScrollViewer"
-                      HorizontalSnapPointsType="{Binding SnapPointsType}"
-                      HorizontalSnapPointsAlignment="{Binding SnapPointsAlignment}"
-                      HorizontalAlignment="Stretch"
-                      Height="350"
-                      HorizontalScrollBarVisibility="Auto"
-                      VerticalScrollBarVisibility="Disabled">
-          <StackPanel AreHorizontalSnapPointsRegular="{Binding AreSnapPointsRegular}"
-                      Orientation="Horizontal"
-                      HorizontalAlignment="Stretch">
-            <Border Padding="5, 30"
-                    Width="300"
-                    BorderBrush="Red"
-                    HorizontalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         VerticalAlignment="Center"
-                         Text="Child 1"/>
-            </Border>
-            <Border Padding="5, 30"
-                    Width="300"
-                    BorderBrush="Red"
-                    VerticalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         VerticalAlignment="Center"
-                         Text="Child 2"/>
-            </Border>
-            <Border Padding="5, 20"
-                    Width="300"
-                    BorderBrush="Red"
-                    VerticalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         VerticalAlignment="Center"
-                         Text="Child 3"/>
-            </Border>
-            <Border Padding="5, 30"
-                    Width="300"
-                    BorderBrush="Red"
-                    VerticalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         VerticalAlignment="Center"
-                         Text="Child 4"/>
-            </Border>
-            <Border Padding="5, 30"
-                    Width="300"
-                    BorderBrush="Red"
-                    VerticalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         VerticalAlignment="Center"
-                         Text="Child 5"/>
-            </Border>
-            <Border Padding="5, 30"
-                    Width="300"
-                    BorderBrush="Red"
-                    VerticalAlignment="Stretch"
-                    BorderThickness="1">
-              <TextBlock FontWeight="Bold"
-                         VerticalAlignment="Center"
-                         Text="Child 6"/>
-            </Border>
-            
-          </StackPanel>
-        </ScrollViewer>
-      </Border>
-    </Grid>
-  </StackPanel>
-</UserControl>

+ 0 - 68
samples/ControlCatalog/Pages/ScrollSnapPage.xaml.cs

@@ -1,68 +0,0 @@
-using System.Collections.Generic;
-using Avalonia.Controls;
-using Avalonia.Controls.Primitives;
-using Avalonia.Markup.Xaml;
-using MiniMvvm;
-
-namespace ControlCatalog.Pages
-{
-    public class ScrollSnapPageViewModel : ViewModelBase
-    {
-        private SnapPointsType _snapPointsType;
-        private SnapPointsAlignment _snapPointsAlignment;
-        private bool _areSnapPointsRegular;
-
-        public ScrollSnapPageViewModel()
-        {
-
-            AvailableSnapPointsType = new List<SnapPointsType>()
-            {
-                SnapPointsType.None,
-                SnapPointsType.Mandatory,
-                SnapPointsType.MandatorySingle
-            };
-
-            AvailableSnapPointsAlignment = new List<SnapPointsAlignment>()
-            {
-                SnapPointsAlignment.Near,
-                SnapPointsAlignment.Center,
-                SnapPointsAlignment.Far,
-            };
-        }
-
-        public bool AreSnapPointsRegular
-        {
-            get => _areSnapPointsRegular;
-            set => this.RaiseAndSetIfChanged(ref _areSnapPointsRegular, value);
-        }
-
-        public SnapPointsType SnapPointsType
-        {
-            get => _snapPointsType;
-            set => this.RaiseAndSetIfChanged(ref _snapPointsType, value);
-        }
-
-        public SnapPointsAlignment SnapPointsAlignment
-        {
-            get => _snapPointsAlignment;
-            set => this.RaiseAndSetIfChanged(ref _snapPointsAlignment, value);
-        }
-        public List<SnapPointsType> AvailableSnapPointsType { get; }
-        public List<SnapPointsAlignment> AvailableSnapPointsAlignment { get; }
-    }
-
-    public class ScrollSnapPage : UserControl
-    {
-        public ScrollSnapPage()
-        {
-            this.InitializeComponent();
-
-            DataContext = new ScrollSnapPageViewModel();
-        }
-
-        private void InitializeComponent()
-        {
-            AvaloniaXamlLoader.Load(this);
-        }
-    }
-}

+ 261 - 29
samples/ControlCatalog/Pages/ScrollViewerPage.xaml

@@ -3,35 +3,267 @@
              xmlns:pages="using:ControlCatalog.Pages"
              x:Class="ControlCatalog.Pages.ScrollViewerPage"
              x:DataType="pages:ScrollViewerPageViewModel">
-  <StackPanel Orientation="Vertical" Spacing="20">
-    <TextBlock TextWrapping="Wrap" Classes="h2">Allows for horizontal and vertical content scrolling. Supports snapping on touch and pointer wheel scrolling.</TextBlock>
-
-    <Grid ColumnDefinitions="Auto, *">
-      <StackPanel Orientation="Vertical" Spacing="4">
-        <ToggleSwitch IsChecked="{Binding AllowAutoHide}" Content="Allow auto hide" />
-        <ToggleSwitch IsChecked="{Binding EnableInertia}" Content="Enable Inertia" />
-
-        <StackPanel Orientation="Vertical" Spacing="4">
-          <TextBlock Text="Horizontal Scroll" />
-          <ComboBox Items="{Binding AvailableVisibility}" SelectedItem="{Binding HorizontalScrollVisibility}" />
-        </StackPanel>
-
-        <StackPanel Orientation="Vertical" Spacing="4">
-          <TextBlock Text="Vertical Scroll" />
-          <ComboBox Items="{Binding AvailableVisibility}" SelectedItem="{Binding VerticalScrollVisibility}" />
-        </StackPanel>
+  <TabControl>
+    <TabItem Header="ScrollViewer">
+      <StackPanel Orientation="Vertical"
+                  Spacing="20">
+        <TextBlock TextWrapping="Wrap"
+                   Classes="h2">Allows for horizontal and vertical content scrolling. Supports snapping on touch and pointer wheel scrolling.</TextBlock>
+
+        <Grid ColumnDefinitions="Auto, *">
+          <StackPanel Orientation="Vertical"
+                      Spacing="4">
+            <ToggleSwitch IsChecked="{Binding AllowAutoHide}"
+                          Content="Allow auto hide" />
+            <ToggleSwitch IsChecked="{Binding EnableInertia}"
+                          Content="Enable Inertia" />
+
+            <StackPanel Orientation="Vertical"
+                        Spacing="4">
+              <TextBlock Text="Horizontal Scroll" />
+              <ComboBox ItemsSource="{Binding AvailableVisibility}"
+                        SelectedItem="{Binding HorizontalScrollVisibility}" />
+            </StackPanel>
+
+            <StackPanel Orientation="Vertical"
+                        Spacing="4">
+              <TextBlock Text="Vertical Scroll" />
+              <ComboBox ItemsSource="{Binding AvailableVisibility}"
+                        SelectedItem="{Binding VerticalScrollVisibility}" />
+            </StackPanel>
+          </StackPanel>
+
+          <ScrollViewer x:Name="ScrollViewer"
+                        Grid.Column="1"
+                        Width="400"
+                        Height="400"
+                        IsScrollInertiaEnabled="{Binding EnableInertia}"
+                        AllowAutoHide="{Binding AllowAutoHide}"
+                        HorizontalScrollBarVisibility="{Binding HorizontalScrollVisibility}"
+                        VerticalScrollBarVisibility="{Binding VerticalScrollVisibility}">
+            <Image Width="800"
+                   Height="800"
+                   Stretch="UniformToFill"
+                   Source="/Assets/delicate-arch-896885_640.jpg" />
+          </ScrollViewer>
+        </Grid>
       </StackPanel>
+    </TabItem>
+    <TabItem Header="Snapping">
+      <StackPanel Orientation="Vertical"
+                  Spacing="4">
+        <TextBlock TextWrapping="Wrap"
+                   Classes="h2">Scrollviewer can snap supported content both vertically and horizontally. Snapping occurs from scrolling with touch or pen.</TextBlock>
+
+        <Grid RowDefinitions="Auto, Auto, Auto, Auto, Auto">
+          <StackPanel Orientation="Horizontal"
+                      Spacing="4">
+            <StackPanel Orientation="Vertical"
+                        Spacing="4">
+              <TextBlock Text="Snap Point Type" />
+              <ComboBox ItemsSource="{Binding AvailableSnapPointsType}"
+                        SelectedItem="{Binding SnapPointsType}" />
+            </StackPanel>
 
-      <ScrollViewer x:Name="ScrollViewer"
-                    Grid.Column="1"
-                    Width="400" Height="400"
-                    IsScrollInertiaEnabled="{Binding EnableInertia}"
-                    AllowAutoHide="{Binding AllowAutoHide}"
-                    HorizontalScrollBarVisibility="{Binding HorizontalScrollVisibility}"
-                    VerticalScrollBarVisibility="{Binding VerticalScrollVisibility}">
-        <Image Width="800" Height="800" Stretch="UniformToFill"
-               Source="/Assets/delicate-arch-896885_640.jpg" />
-      </ScrollViewer>
-    </Grid>
-  </StackPanel>
+            <StackPanel Orientation="Vertical"
+                        Spacing="4">
+              <TextBlock Text="Snap Point Alignment" />
+              <ComboBox ItemsSource="{Binding AvailableSnapPointsAlignment}"
+                        SelectedItem="{Binding SnapPointsAlignment}" />
+            </StackPanel>
+
+            <ToggleSwitch IsChecked="{Binding AreSnapPointsRegular}"
+                          OffContent="No"
+                          OnContent="Yes"
+                          Content="Are Snap Points regular?" />
+          </StackPanel>
+          <TextBlock TextWrapping="Wrap"
+                     Grid.Row="1"
+                     Margin="0,10"
+                     Classes="h2">Vertical Snapping</TextBlock>
+
+          <Border
+            BorderBrush="Green"
+            BorderThickness="1"
+            Padding="0"
+            Grid.Row="2"
+            Margin="10, 5">
+            <ScrollViewer x:Name="VerticalSnapsScrollViewer"
+                          VerticalSnapPointsType="{Binding SnapPointsType}"
+                          VerticalSnapPointsAlignment="{Binding SnapPointsAlignment}"
+                          HorizontalAlignment="Stretch"
+                          Height="350"
+                          HorizontalScrollBarVisibility="Disabled">
+              <StackPanel AreVerticalSnapPointsRegular="{Binding AreSnapPointsRegular}"
+                          Orientation="Vertical"
+                          HorizontalAlignment="Stretch">
+                <Border Padding="5, 30"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 1"/>
+                </Border>
+                <Border Padding="5, 30"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 2"/>
+                </Border>
+                <Border Padding="5, 20"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 3"/>
+                </Border>
+                <Border Padding="5, 30"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 4"/>
+                </Border>
+                <Border Padding="5, 30"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 5"/>
+                </Border>
+                <Border Padding="5, 30"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 6"/>
+                </Border>
+                <Border Padding="5,8"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 7"/>
+                </Border>
+                <Border Padding="5, 30"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 8"/>
+                </Border>
+                <Border Padding="5,4"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 9"/>
+                </Border>
+                <Border Padding="5, 30"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 20"/>
+                </Border>
+                <Border Padding="5, 30"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 11"/>
+                </Border>
+                <Border Padding="5, 30"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             Text="Child 12"/>
+                </Border>
+              </StackPanel>
+            </ScrollViewer>
+          </Border>
+          <TextBlock TextWrapping="Wrap"
+                     Grid.Row="3"
+                     Margin="0,10"
+                     Classes="h2">Horizontal Snapping</TextBlock>
+          <Border
+            BorderBrush="Green"
+            BorderThickness="1"
+            Padding="0"
+            Grid.Row="4"
+            Margin="10, 10">
+            <ScrollViewer x:Name="HorizontalSnapsScrollViewer"
+                          HorizontalSnapPointsType="{Binding SnapPointsType}"
+                          HorizontalSnapPointsAlignment="{Binding SnapPointsAlignment}"
+                          HorizontalAlignment="Stretch"
+                          Height="350"
+                          HorizontalScrollBarVisibility="Auto"
+                          VerticalScrollBarVisibility="Disabled">
+              <StackPanel AreHorizontalSnapPointsRegular="{Binding AreSnapPointsRegular}"
+                          Orientation="Horizontal"
+                          HorizontalAlignment="Stretch">
+                <Border Padding="5, 30"
+                        Width="300"
+                        BorderBrush="Red"
+                        HorizontalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             VerticalAlignment="Center"
+                             Text="Child 1"/>
+                </Border>
+                <Border Padding="5, 30"
+                        Width="300"
+                        BorderBrush="Red"
+                        VerticalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             VerticalAlignment="Center"
+                             Text="Child 2"/>
+                </Border>
+                <Border Padding="5, 20"
+                        Width="300"
+                        BorderBrush="Red"
+                        VerticalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             VerticalAlignment="Center"
+                             Text="Child 3"/>
+                </Border>
+                <Border Padding="5, 30"
+                        Width="300"
+                        BorderBrush="Red"
+                        VerticalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             VerticalAlignment="Center"
+                             Text="Child 4"/>
+                </Border>
+                <Border Padding="5, 30"
+                        Width="300"
+                        BorderBrush="Red"
+                        VerticalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             VerticalAlignment="Center"
+                             Text="Child 5"/>
+                </Border>
+                <Border Padding="5, 30"
+                        Width="300"
+                        BorderBrush="Red"
+                        VerticalAlignment="Stretch"
+                        BorderThickness="1">
+                  <TextBlock FontWeight="Bold"
+                             VerticalAlignment="Center"
+                             Text="Child 6"/>
+                </Border>
+
+              </StackPanel>
+            </ScrollViewer>
+          </Border>
+        </Grid>
+      </StackPanel>
+    </TabItem>
+  </TabControl>  
 </UserControl>

+ 37 - 0
samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs

@@ -12,6 +12,9 @@ namespace ControlCatalog.Pages
         private bool _enableInertia;
         private ScrollBarVisibility _horizontalScrollVisibility;
         private ScrollBarVisibility _verticalScrollVisibility;
+        private SnapPointsType _snapPointsType;
+        private SnapPointsAlignment _snapPointsAlignment;
+        private bool _areSnapPointsRegular;
 
         public ScrollViewerPageViewModel()
         {
@@ -23,6 +26,20 @@ namespace ControlCatalog.Pages
                 ScrollBarVisibility.Disabled,
             };
 
+            AvailableSnapPointsType = new List<SnapPointsType>()
+            {
+                SnapPointsType.None,
+                SnapPointsType.Mandatory,
+                SnapPointsType.MandatorySingle
+            };
+
+            AvailableSnapPointsAlignment = new List<SnapPointsAlignment>()
+            {
+                SnapPointsAlignment.Near,
+                SnapPointsAlignment.Center,
+                SnapPointsAlignment.Far,
+            };
+
             HorizontalScrollVisibility = ScrollBarVisibility.Auto;
             VerticalScrollVisibility = ScrollBarVisibility.Auto;
             AllowAutoHide = true;
@@ -54,6 +71,26 @@ namespace ControlCatalog.Pages
         }
 
         public List<ScrollBarVisibility> AvailableVisibility { get; }
+
+        public bool AreSnapPointsRegular
+        {
+            get => _areSnapPointsRegular;
+            set => this.RaiseAndSetIfChanged(ref _areSnapPointsRegular, value);
+        }
+
+        public SnapPointsType SnapPointsType
+        {
+            get => _snapPointsType;
+            set => this.RaiseAndSetIfChanged(ref _snapPointsType, value);
+        }
+
+        public SnapPointsAlignment SnapPointsAlignment
+        {
+            get => _snapPointsAlignment;
+            set => this.RaiseAndSetIfChanged(ref _snapPointsAlignment, value);
+        }
+        public List<SnapPointsType> AvailableSnapPointsType { get; }
+        public List<SnapPointsAlignment> AvailableSnapPointsAlignment { get; }
     }
 
     public class ScrollViewerPage : UserControl

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

@@ -51,7 +51,7 @@
                     Text="From DataTemplate">
                 </TextBlock>
                 <TabControl
-                    Items="{Binding Tabs}"
+                    ItemsSource="{Binding Tabs}"
                     Margin="0 16"
                     DisplayMemberBinding="{Binding Header, x:DataType=viewModels:TabControlPageViewModelItem}"
                     TabStripPlacement="{Binding TabPlacement}">

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

@@ -18,7 +18,7 @@
         <Separator Margin="0 16"/>
 
         <TextBlock Classes="h1">Dynamically generated</TextBlock>
-        <TabStrip Items="{Binding Tabs}">
+        <TabStrip ItemsSource="{Binding Tabs}">
             <TabStrip.Styles>
                 <Style Selector="TabStripItem" x:DataType="viewModels:TabControlPageViewModelItem">
                     <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>

+ 8 - 12
samples/ControlCatalog/Pages/ThemePage.axaml.cs

@@ -1,35 +1,31 @@
-using Avalonia;
-using Avalonia.Controls;
+using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 using Avalonia.Styling;
 
 namespace ControlCatalog.Pages
 {
-    public class ThemePage : UserControl
+    public partial class ThemePage : UserControl
     {
         public static ThemeVariant Pink { get; } = new("Pink", ThemeVariant.Light);
         
         public ThemePage()
         {
-            AvaloniaXamlLoader.Load(this);
+            InitializeComponent();
 
-            var selector = this.FindControl<ComboBox>("Selector")!;
-            var themeVariantScope = this.FindControl<ThemeVariantScope>("ThemeVariantScope")!;
-
-            selector.Items = new[]
+            Selector.ItemsSource = new[]
             {
                 ThemeVariant.Default,
                 ThemeVariant.Dark,
                 ThemeVariant.Light,
                 Pink
             };
-            selector.SelectedIndex = 0;
+            Selector.SelectedIndex = 0;
 
-            selector.SelectionChanged += (_, _) =>
+            Selector.SelectionChanged += (_, _) =>
             {
-                if (selector.SelectedItem is ThemeVariant theme)
+                if (Selector.SelectedItem is ThemeVariant theme)
                 {
-                    themeVariantScope.RequestedThemeVariant = theme;
+                    ThemeVariantScope.RequestedThemeVariant = theme;
                 }
             };
         }

+ 1 - 1
samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml

@@ -53,7 +53,7 @@
             
             <StackPanel Margin="5" Spacing="5" Grid.IsSharedSizeScope="True">
                 <HeaderedContentControl Header="Select a transition">
-                     <ComboBox Items="{Binding PageTransitions}" SelectedItem="{Binding SelectedTransition}" />
+                     <ComboBox ItemsSource="{Binding PageTransitions}" SelectedItem="{Binding SelectedTransition}" />
                 </HeaderedContentControl>
                 <HeaderedContentControl Header="Duration">
                      <NumericUpDown Value="{Binding Duration}" Increment="250" Minimum="100" />

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

@@ -11,7 +11,7 @@
                 HorizontalAlignment="Center"
                 Spacing="16">
       <StackPanel Orientation="Vertical" Spacing="8">
-        <TreeView Items="{Binding Items}" SelectedItems="{Binding SelectedItems}" SelectionMode="{Binding SelectionMode}" Width="250" Height="350">
+        <TreeView ItemsSource="{Binding Items}" SelectedItems="{Binding SelectedItems}" SelectionMode="{Binding SelectionMode}" Width="250" Height="350">
           <TreeView.ItemTemplate>
             <TreeDataTemplate ItemsSource="{Binding Children}">
               <TextBlock Text="{Binding Header}"/>

+ 7 - 0
samples/Generators.Sandbox/App.xaml

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

+ 20 - 0
samples/Generators.Sandbox/App.xaml.cs

@@ -0,0 +1,20 @@
+using Avalonia;
+using Avalonia.Markup.Xaml;
+using Generators.Sandbox.ViewModels;
+
+namespace Generators.Sandbox;
+
+public class App : Application
+{
+    public override void Initialize() => AvaloniaXamlLoader.Load(this);
+
+    public override void OnFrameworkInitializationCompleted()
+    {
+        var view = new Views.SignUpView
+        {
+            ViewModel = new SignUpViewModel()
+        };
+        view.Show();
+        base.OnFrameworkInitializationCompleted();
+    }
+}

+ 10 - 0
samples/Generators.Sandbox/Controls/CustomTextBox.cs

@@ -0,0 +1,10 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Styling;
+
+namespace Generators.Sandbox.Controls;
+
+public class CustomTextBox : TextBox, IStyleable
+{
+    Type IStyleable.StyleKey => typeof(TextBox);
+}

+ 45 - 0
samples/Generators.Sandbox/Controls/SignUpView.xaml

@@ -0,0 +1,45 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:controls="using:Generators.Sandbox.Controls"
+        x:Class="Generators.Sandbox.Controls.SignUpView">
+    <StackPanel>
+        <controls:CustomTextBox Margin="0 10 0 0"
+                 x:Name="UserNameTextBox"
+                 Watermark="Please, enter user name..."
+                 UseFloatingWatermark="True" />
+        <TextBlock x:Name="UserNameValidation" 
+                   Foreground="Red"
+                   FontSize="12" />
+        <TextBox Margin="0 10 0 0"
+                 x:Name="PasswordTextBox"
+                 Watermark="Please, enter your password..."
+                 UseFloatingWatermark="True"
+                 PasswordChar="*" />
+        <TextBlock x:Name="PasswordValidation" 
+                   Foreground="Red"
+                   FontSize="12" />
+        <TextBox Margin="0 10 0 0"
+                 x:Name="ConfirmPasswordTextBox"
+                 Watermark="Please, confirm the password..."
+                 UseFloatingWatermark="True"
+                 PasswordChar="*" />
+        <TextBlock x:Name="ConfirmPasswordValidation"
+                   TextWrapping="Wrap"
+                   Foreground="Red"
+                   FontSize="12" />
+        <TextBlock>
+            <TextBlock.Inlines>
+                <InlineCollection>
+                    <Run x:Name="SignUpButtonDescription" />
+                </InlineCollection>
+            </TextBlock.Inlines>
+        </TextBlock>
+        <Button Margin="0 10 0 5"
+                Content="Sign up"
+                x:Name="SignUpButton" />
+        <TextBlock x:Name="CompoundValidation"
+                   TextWrapping="Wrap"
+                   Foreground="Red"
+                   FontSize="12" />
+    </StackPanel>
+</UserControl>

+ 54 - 0
samples/Generators.Sandbox/Controls/SignUpView.xaml.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Reactive.Disposables;
+using Avalonia.ReactiveUI;
+using Generators.Sandbox.ViewModels;
+using ReactiveUI;
+using ReactiveUI.Validation.Extensions;
+using ReactiveUI.Validation.Formatters;
+
+namespace Generators.Sandbox.Controls;
+
+/// <summary>
+/// This is a sample view class with typed x:Name references generated using
+/// .NET 5 source generators. The class has to be partial because x:Name
+/// references are living in a separate partial class file. See also:
+/// https://devblogs.microsoft.com/dotnet/new-c-source-generator-samples/
+/// </summary>
+public partial class SignUpView : ReactiveUserControl<SignUpViewModel>
+{
+    public SignUpView()
+    {
+        // The InitializeComponent method is also generated automatically
+        // and lives in the autogenerated part of the partial class.
+        InitializeComponent();
+        this.WhenActivated(disposables =>
+        {
+            this.Bind(ViewModel, x => x.UserName, x => x.UserNameTextBox.Text)
+                .DisposeWith(disposables);
+            this.Bind(ViewModel, x => x.Password, x => x.PasswordTextBox.Text)
+                .DisposeWith(disposables);
+            this.Bind(ViewModel, x => x.ConfirmPassword, x => x.ConfirmPasswordTextBox.Text)
+                .DisposeWith(disposables);
+            this.BindCommand(ViewModel, x => x.SignUp, x => x.SignUpButton)
+                .DisposeWith(disposables);
+
+            this.BindValidation(ViewModel, x => x.UserName, x => x.UserNameValidation.Text)
+                .DisposeWith(disposables);
+            this.BindValidation(ViewModel, x => x.Password, x => x.PasswordValidation.Text)
+                .DisposeWith(disposables);
+            this.BindValidation(ViewModel, x => x.ConfirmPassword, x => x.ConfirmPasswordValidation.Text)
+                .DisposeWith(disposables);
+
+            var newLineFormatter = new SingleLineFormatter(Environment.NewLine);
+            this.BindValidation(ViewModel, x => x.CompoundValidation.Text, newLineFormatter)
+                .DisposeWith(disposables);
+
+            // The references to text boxes below are also auto generated.
+            // Use Ctrl+Click in order to view the generated sources. 
+            UserNameTextBox.Text = "Joseph!";
+            PasswordTextBox.Text = "1234";
+            ConfirmPasswordTextBox.Text = "1234";
+            SignUpButtonDescription.Text = "Press the button below to sign up.";
+        });
+    }
+}

+ 28 - 0
samples/Generators.Sandbox/Generators.Sandbox.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <IncludeAvaloniaGenerators>true</IncludeAvaloniaGenerators>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <AvaloniaResource Include="**\*.xaml"/>
+    <!-- Note this AdditionalFiles directive. -->
+    <AdditionalFiles Include="**\*.xaml"/>
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="ReactiveUI.Validation" Version="3.0.22"/>
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj"/>
+    <ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj"/>
+    <ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj"/>
+    <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj"/>
+    <ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj"/>
+  </ItemGroup>
+
+  <Import Project="..\..\build\BuildTargets.targets"/>
+  <Import Project="..\..\build\SourceGenerators.props"/>
+</Project>

+ 15 - 0
samples/Generators.Sandbox/Program.cs

@@ -0,0 +1,15 @@
+using Avalonia;
+using Avalonia.ReactiveUI;
+
+namespace Generators.Sandbox;
+
+internal static class Program
+{
+    public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
+
+    private static AppBuilder BuildAvaloniaApp()
+        => AppBuilder.Configure<App>()
+            .UseReactiveUI()
+            .UsePlatformDetect()
+            .LogToTrace();
+}

+ 70 - 0
samples/Generators.Sandbox/ViewModels/SignUpViewModel.cs

@@ -0,0 +1,70 @@
+using System.Reactive;
+using ReactiveUI;
+using ReactiveUI.Validation.Extensions;
+using ReactiveUI.Validation.Helpers;
+
+namespace Generators.Sandbox.ViewModels;
+
+public class SignUpViewModel : ReactiveValidationObject
+{
+    private string _userName = string.Empty;
+    private string _password = string.Empty;
+    private string _confirmPassword = string.Empty;
+
+    public SignUpViewModel()
+    {
+        this.ValidationRule(
+            vm => vm.UserName,
+            name => !string.IsNullOrWhiteSpace(name),
+            "UserName is required.");
+
+        this.ValidationRule(
+            vm => vm.Password,
+            password => !string.IsNullOrWhiteSpace(password),
+            "Password is required.");
+
+        this.ValidationRule(
+            vm => vm.Password,
+            password => password?.Length > 2,
+            password => $"Password should be longer, current length: {password.Length}");
+
+        this.ValidationRule(
+            vm => vm.ConfirmPassword,
+            confirmation => !string.IsNullOrWhiteSpace(confirmation),
+            "Confirm password field is required.");
+
+        var passwordsObservable =
+            this.WhenAnyValue(
+                x => x.Password,
+                x => x.ConfirmPassword,
+                (password, confirmation) =>
+                    password == confirmation);
+
+        this.ValidationRule(
+            vm => vm.ConfirmPassword,
+            passwordsObservable,
+            "Passwords must match.");
+
+        SignUp = ReactiveCommand.Create(() => {}, this.IsValid());
+    }
+
+    public ReactiveCommand<Unit, Unit> SignUp { get; }
+
+    public string UserName
+    {
+        get => _userName;
+        set => this.RaiseAndSetIfChanged(ref _userName, value);
+    }
+
+    public string Password
+    {
+        get => _password;
+        set => this.RaiseAndSetIfChanged(ref _password, value);
+    }
+
+    public string ConfirmPassword
+    {
+        get => _confirmPassword;
+        set => this.RaiseAndSetIfChanged(ref _confirmPassword, value);
+    }
+}

+ 9 - 0
samples/Generators.Sandbox/Views/SignUpView.xaml

@@ -0,0 +1,9 @@
+<Window xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:controls="using:Generators.Sandbox.Controls"
+        x:Class="Generators.Sandbox.Views.SignUpView">
+    <StackPanel Margin="10">
+        <TextBlock Text="Sign Up" />
+        <controls:SignUpView x:Name="SignUpControl" />
+    </StackPanel>
+</Window>

+ 28 - 0
samples/Generators.Sandbox/Views/SignUpView.xaml.cs

@@ -0,0 +1,28 @@
+using System.Reactive.Disposables;
+using Avalonia.ReactiveUI;
+using Generators.Sandbox.ViewModels;
+using ReactiveUI;
+
+namespace Generators.Sandbox.Views;
+
+/// <summary>
+/// This is a sample view class with typed x:Name references generated using
+/// .NET 5 source generators. The class has to be partial because x:Name
+/// references are living in a separate partial class file. See also:
+/// https://devblogs.microsoft.com/dotnet/new-c-source-generator-samples/
+/// </summary>
+public partial class SignUpView : ReactiveWindow<SignUpViewModel>
+{
+    public SignUpView()
+    {
+        // The InitializeComponent method is also generated automatically
+        // and lives in the autogenerated part of the partial class.
+        InitializeComponent();
+        this.WhenActivated(disposables =>
+        {
+            this.WhenAnyValue(view => view.ViewModel)
+                .BindTo(this, view => view.SignUpControl.ViewModel)
+                .DisposeWith(disposables);
+        });
+    }
+}

+ 23 - 3
samples/IntegrationTestApp/MainWindow.axaml

@@ -109,7 +109,7 @@
           <StackPanel DockPanel.Dock="Bottom">
             <Button Name="ListBoxSelectionClear">Clear Selection</Button>
           </StackPanel>
-          <ListBox Name="BasicListBox" Items="{Binding ListBoxItems}" SelectionMode="Multiple"/>
+          <ListBox Name="BasicListBox" ItemsSource="{Binding ListBoxItems}" SelectionMode="Multiple"/>
         </DockPanel>
       </TabItem>
       
@@ -151,6 +151,12 @@
               <ComboBoxItem Name="ShowWindowStateMaximized">Maximized</ComboBoxItem>
               <ComboBoxItem Name="ShowWindowStateFullScreen">FullScreen</ComboBoxItem>
             </ComboBox>
+            <ComboBox Name="ShowWindowSystemDecorations" SelectedIndex="2">
+              <ComboBoxItem Name="ShowWindowSystemDecorationsNone">None</ComboBoxItem>
+              <ComboBoxItem Name="ShowWindowSystemDecorationsBorderOnly">BorderOnly</ComboBoxItem>
+              <ComboBoxItem Name="ShowWindowSystemDecorationsFull">Full</ComboBoxItem>
+            </ComboBox>
+            <CheckBox Name="ShowWindowExtendClientAreaToDecorationsHint">ExtendClientAreaToDecorationsHint</CheckBox>
             <CheckBox Name="ShowWindowCanResize" IsChecked="True">Can Resize</CheckBox>
             <Button Name="ShowWindow">Show Window</Button>
             <Button Name="SendToBack">Send to Back</Button>
@@ -164,8 +170,22 @@
           </StackPanel>
         </Grid>
       </TabItem>
-      <TabItem Header="SliderTab">
-          <Slider VerticalAlignment="Top" Name="Slider" Value="30"/>
+      
+      <TabItem Header="Slider">
+          <DockPanel>
+            <DockPanel DockPanel.Dock="Top">
+              <TextBox Name="HorizontalSliderValue"
+                       DockPanel.Dock="Right"
+                       Text="{Binding #HorizontalSlider.Value, Mode=OneWay, StringFormat=\{0:0\}}"
+                       VerticalAlignment="Top"/>
+              <Slider Name="HorizontalSlider" Value="50"/>
+            </DockPanel>
+            <Button Name="ResetSliders">Reset</Button>
+          </DockPanel>
+      </TabItem>
+      
+      <TabItem Header="ScrollBar">
+        <ScrollBar Name="MyScrollBar" Orientation="Horizontal" AllowAutoHide="False" Width="200" Height="30" Value="20"/>
       </TabItem>
     </TabControl>
   </DockPanel>

+ 7 - 1
samples/IntegrationTestApp/MainWindow.axaml.cs

@@ -68,6 +68,8 @@ namespace IntegrationTestApp
             var locationComboBox = this.GetControl<ComboBox>("ShowWindowLocation");
             var stateComboBox = this.GetControl<ComboBox>("ShowWindowState");
             var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null;
+            var systemDecorations = this.GetControl<ComboBox>("ShowWindowSystemDecorations");
+            var extendClientArea = this.GetControl<CheckBox>("ShowWindowExtendClientAreaToDecorationsHint");
             var canResizeCheckBox = this.GetControl<CheckBox>("ShowWindowCanResize");
             var owner = (Window)this.GetVisualRoot()!;
 
@@ -95,6 +97,8 @@ namespace IntegrationTestApp
             }
 
             sizeTextBox.Text = string.Empty;
+            window.ExtendClientAreaToDecorationsHint = extendClientArea.IsChecked ?? false;
+            window.SystemDecorations = (SystemDecorations)systemDecorations.SelectedIndex;
             window.WindowState = (WindowState)stateComboBox.SelectedIndex;
 
             switch (modeComboBox.SelectedIndex)
@@ -158,7 +162,7 @@ namespace IntegrationTestApp
             var popup = new Popup
             {
                 WindowManagerAddShadowHint = false,
-                PlacementMode = PlacementMode.AnchorAndGravity,
+                Placement = PlacementMode.AnchorAndGravity,
                 PlacementAnchor = PopupAnchor.Top,
                 PlacementGravity = PopupGravity.Bottom,
                 Width= 200,
@@ -266,6 +270,8 @@ namespace IntegrationTestApp
                 this.Get<ListBox>("BasicListBox").SelectedIndex = -1;
             if (source?.Name == "MenuClickedMenuItemReset")
                 this.Get<TextBlock>("ClickedMenuItem").Text = "None";
+            if (source?.Name == "ResetSliders")
+                this.Get<Slider>("HorizontalSlider").Value = 50;
             if (source?.Name == "ShowTransparentWindow")
                 ShowTransparentWindow();
             if (source?.Name == "ShowTransparentPopup")

+ 18 - 6
samples/IntegrationTestApp/ShowWindowTest.axaml

@@ -6,7 +6,7 @@
         x:DataType="Window"
         Title="Show Window Test">
   <integrationTestApp:MeasureBorder Name="MyBorder">
-    <Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
+    <Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
       <Label Grid.Column="0" Grid.Row="1">Client Size</Label>
       <TextBox Name="CurrentClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True"
                Text="{Binding ClientSize, Mode=OneWay}" />
@@ -35,13 +35,25 @@
         <ComboBoxItem Name="WindowStateFullScreen">FullScreen</ComboBoxItem>
       </ComboBox>
 
-      <Label Grid.Column="0" Grid.Row="8">Order (mac)</Label>
-      <TextBox Name="CurrentOrder" Grid.Column="1" Grid.Row="8" IsReadOnly="True" />
+      <Label Grid.Column="0" Grid.Row="8">SystemDecorations</Label>
+      <ComboBox Name="CurrentSystemDecorations" Grid.Column="1" Grid.Row="8"  SelectedIndex="{Binding SystemDecorations}">
+        <ComboBoxItem Name="SystemDecorationsNone">None</ComboBoxItem>
+        <ComboBoxItem Name="SystemDecorationsBorderOnly">BorderOnly</ComboBoxItem>
+        <ComboBoxItem Name="SystemDecorationsFull">Full</ComboBoxItem>
+      </ComboBox>
+
+      <CheckBox Name="CurrentExtendClientAreaToDecorationsHint" Grid.ColumnSpan="2" Grid.Row="9"
+                IsChecked="{Binding ExtendClientAreaToDecorationsHint}">
+        ExtendClientAreaToDecorationsHint
+      </CheckBox>
+
+      <Label Grid.Column="0" Grid.Row="10">Order (mac)</Label>
+      <TextBox Name="CurrentOrder" Grid.Column="1" Grid.Row="10" IsReadOnly="True" />
       
-      <Label Grid.Row="9" Content="MeasuredWith:" />
-      <TextBlock Grid.Column="1" Grid.Row="9" Name="CurrentMeasuredWithText" Text="{Binding #MyBorder.MeasuredWith}" />
+      <Label Grid.Row="11" Content="MeasuredWith:" />
+      <TextBlock Grid.Column="1" Grid.Row="11" Name="CurrentMeasuredWithText" Text="{Binding #MyBorder.MeasuredWith}" />
 
-      <Button Name="HideButton" Grid.Row="10" Command="{Binding $parent[Window].Hide}">Hide</Button>
+      <Button Name="HideButton" Grid.Row="12" Command="{Binding $parent[Window].Hide}">Hide</Button>
       
     </Grid>
   </integrationTestApp:MeasureBorder>

+ 40 - 0
samples/RenderDemo/Pages/AnimationsPage.xaml

@@ -308,6 +308,41 @@
           </Animation>
         </Style.Animations>
       </Style>
+      <Style Selector="Border.Blur">
+        <Style.Animations>
+          <Animation Duration="0:0:3"
+                     IterationCount="Infinite"
+                     PlaybackDirection="Alternate">
+            <KeyFrame Cue="0%">
+              <Setter Property="Effect" Value="blur(0)"/>
+            </KeyFrame>
+            <KeyFrame Cue="100%">
+              <Setter Property="Effect" Value="blur(10)"/>
+            </KeyFrame>
+          </Animation>
+        </Style.Animations>
+        <Setter Property="Child" Value="{StaticResource Acorn}"/>
+      </Style>
+      <Style Selector="Border.DropShadow">
+        <Style.Animations>
+          <Animation Duration="0:0:3"
+                     IterationCount="Infinite"
+                     PlaybackDirection="Alternate">
+            <KeyFrame Cue="0%">
+              <Setter Property="Effect" Value="drop-shadow(0 0 0)"/>
+            </KeyFrame>
+            <KeyFrame Cue="35%">
+              <Setter Property="Effect" Value="drop-shadow(5 5 0 Green)"/>
+            </KeyFrame>
+            <KeyFrame Cue="70%">
+              <Setter Property="Effect" Value="drop-shadow(5 5 5 Red)"/>
+            </KeyFrame>
+            <KeyFrame Cue="100%">
+              <Setter Property="Effect" Value="drop-shadow(20 -5 5 Blue)"/>
+            </KeyFrame>
+          </Animation>
+        </Style.Animations>
+      </Style>
     </Styles>
   </UserControl.Styles>
   <Grid>
@@ -332,6 +367,11 @@
         <Border Classes="Test Rect8" Child="{x:Null}" />
         <Border Classes="Test Rect9" Child="{x:Null}" />
         <Border Classes="Test Rect10" Child="{x:Null}" />
+        <Border Classes="Test Blur" Background="#ffa0a0a0" BorderThickness="4" BorderBrush="Yellow" Padding="10"/>
+        <Border Classes="Test DropShadow" Background="Transparent" BorderThickness="4" BorderBrush="Yellow">
+          <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Drop
+            Shadow</TextBlock>
+        </Border>
       </WrapPanel>
     </StackPanel>
   </Grid>

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

@@ -29,7 +29,7 @@ namespace RenderDemo.Pages
         public override void Render(DrawingContext context)
         {
             using (var ctx = _bitmap.CreateDrawingContext())
-            using (ctx.PushPostTransform(Matrix.CreateTranslation(-100, -100)
+            using (ctx.PushTransform(Matrix.CreateTranslation(-100, -100)
                                          * Matrix.CreateRotation(_st.Elapsed.TotalSeconds)
                                          * Matrix.CreateTranslation(100, 100)))
             {

BIN
samples/SafeAreaDemo.Android/Icon.png


+ 11 - 0
samples/SafeAreaDemo.Android/MainActivity.cs

@@ -0,0 +1,11 @@
+using Android.App;
+using Android.Content.PM;
+using Avalonia.Android;
+
+namespace SafeAreaDemo.Android
+{
+    [Activity(Label = "SafeAreaDemo.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
+    public class MainActivity : AvaloniaMainActivity
+    {
+    }
+}

+ 5 - 0
samples/SafeAreaDemo.Android/Properties/AndroidManifest.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
+	<uses-permission android:name="android.permission.INTERNET" />
+	<application android:label="SafeAreaDemo" android:icon="@drawable/Icon" />
+</manifest>

+ 13 - 0
samples/SafeAreaDemo.Android/Resources/drawable/splash_screen.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+  <item>
+    <color android:color="@color/splash_background"/>
+  </item>
+
+  <item android:drawable="@drawable/icon"
+        android:width="120dp"
+        android:height="120dp"
+        android:gravity="center" />
+
+</layer-list>

+ 4 - 0
samples/SafeAreaDemo.Android/Resources/values/colors.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <color name="splash_background">#FFFFFF</color>
+</resources>

+ 17 - 0
samples/SafeAreaDemo.Android/Resources/values/styles.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<resources>
+
+  <style name="MyTheme">
+  </style>
+
+  <style name="MyTheme.NoActionBar" parent="@style/Theme.AppCompat.NoActionBar">
+    <item name="android:windowActionBar">false</item>
+    <item name="android:windowNoTitle">true</item>
+  </style>
+
+  <style name="MyTheme.Splash" parent ="MyTheme.NoActionBar">
+    <item name="android:windowBackground">@drawable/splash_screen</item>
+    <item name="android:windowContentOverlay">@null</item>
+  </style>
+
+</resources>

+ 24 - 0
samples/SafeAreaDemo.Android/SafeAreaDemo.Android.csproj

@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net7.0-android</TargetFramework>
+    <SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
+    <Nullable>enable</Nullable>
+    <ApplicationId>com.avalonia.safeareademo</ApplicationId>
+    <ApplicationVersion>1</ApplicationVersion>
+    <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
+    <AndroidPackageFormat>apk</AndroidPackageFormat>
+    <AndroidEnableProfiledAot>False</AndroidEnableProfiledAot>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <AndroidResource Include="Icon.png">
+      <Link>Resources\drawable\Icon.png</Link>
+    </AndroidResource>
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\SafeAreaDemo\SafeAreaDemo.csproj" />
+    <ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj" />
+  </ItemGroup>
+</Project>

+ 30 - 0
samples/SafeAreaDemo.Android/SplashActivity.cs

@@ -0,0 +1,30 @@
+using Android.App;
+using Android.Content;
+using Android.OS;
+using Avalonia;
+using Avalonia.Android;
+using Application = Android.App.Application;
+
+namespace SafeAreaDemo.Android
+{
+    [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
+    public class SplashActivity : AvaloniaSplashActivity<App>
+    {
+        protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
+        {
+            return base.CustomizeAppBuilder(builder);
+        }
+
+        protected override void OnCreate(Bundle? savedInstanceState)
+        {
+            base.OnCreate(savedInstanceState);
+        }
+
+        protected override void OnResume()
+        {
+            base.OnResume();
+
+            StartActivity(new Intent(Application.Context, typeof(MainActivity)));
+        }
+    }
+}

+ 21 - 0
samples/SafeAreaDemo.Desktop/Program.cs

@@ -0,0 +1,21 @@
+using Avalonia;
+using System;
+
+namespace SafeAreaDemo.Desktop
+{
+    internal 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()
+            .StartWithClassicDesktopLifetime(args);
+
+        // Avalonia configuration, don't remove; also used by visual designer.
+        public static AppBuilder BuildAvaloniaApp()
+            => AppBuilder.Configure<App>()
+                .UsePlatformDetect()
+                .LogToTrace();
+    }
+}

+ 24 - 0
samples/SafeAreaDemo.Desktop/SafeAreaDemo.Desktop.csproj

@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <!--If you are willing to use Windows/MacOS native APIs you will need to create 3 projects.
+    One for Windows with net7.0-windows TFM, one for MacOS with net7.0-macos and one with net7.0 TFM for Linux.-->
+    <TargetFramework>net7.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
+    <ProjectReference Include="..\..\src\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" />
+  </ItemGroup>
+  <Import Project="..\..\build\SampleApp.props" />
+  <Import Project="..\..\build\ReferenceCoreLibraries.props" />
+</Project>

+ 18 - 0
samples/SafeAreaDemo.Desktop/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="SafeAreaDemo.Desktop"/>
+
+  <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>

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません