Browse Source

Merge branch 'master' into feature/mouse_over_events

pull/7804/head
Dariusz Komosiński 5 years ago
committed by GitHub
parent
commit
1ecb657605
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .editorconfig
  2. 3
      .gitignore
  3. 5
      .ncrunch/Avalonia.IntegrationTests.Appium.v3.ncrunchproject
  4. 5
      .ncrunch/Avalonia.IntegrationTests.Win32.v3.ncrunchproject
  5. 5
      .ncrunch/IntegrationTestApp.v3.ncrunchproject
  6. 132
      Avalonia.sln
  7. 8
      azure-pipelines.yml
  8. 1
      build/CoreLibraries.props
  9. 6
      build/HarfBuzzSharp.props
  10. 2
      build/SharedVersion.props
  11. 6
      build/SkiaSharp.props
  12. 18
      build/XUnit.props
  13. 9
      dirs.proj
  14. 3
      global.json
  15. 8
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  16. 1
      native/Avalonia.Native/src/OSX/AvnString.h
  17. 16
      native/Avalonia.Native/src/OSX/AvnString.mm
  18. 12
      native/Avalonia.Native/src/OSX/automation.h
  19. 496
      native/Avalonia.Native/src/OSX/automation.mm
  20. 1
      native/Avalonia.Native/src/OSX/common.h
  21. 12
      native/Avalonia.Native/src/OSX/main.mm
  22. 1
      native/Avalonia.Native/src/OSX/window.h
  23. 54
      native/Avalonia.Native/src/OSX/window.mm
  24. 5
      nukebuild/numerge.config
  25. 2
      readme.md
  26. 16
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  27. 13
      samples/ControlCatalog.Android/MainActivity.cs
  28. 2
      samples/ControlCatalog.Android/Resources/values/styles.xml
  29. 10
      samples/ControlCatalog.Android/SplashActivity.cs
  30. 15
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  31. 7
      samples/ControlCatalog.NetCore/Program.cs
  32. 7
      samples/ControlCatalog.NetCore/rd.xml
  33. 190
      samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
  34. 9
      samples/ControlCatalog.iOS/Info.plist
  35. 4
      samples/ControlCatalog.iOS/Main.cs
  36. 36
      samples/ControlCatalog.iOS/Properties/AssemblyInfo.cs
  37. 2
      samples/ControlCatalog.iOS/Resources/LaunchScreen.xib
  38. 5
      samples/ControlCatalog/App.xaml.cs
  39. 1
      samples/ControlCatalog/ControlCatalog.csproj
  40. 3
      samples/ControlCatalog/MainView.xaml
  41. 23
      samples/ControlCatalog/Pages/ClipboardPage.xaml
  42. 77
      samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
  43. 8
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  44. 4
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  45. 2
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  46. 11
      samples/ControlCatalog/Pages/TextBlockPage.xaml
  47. 3
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  48. 6
      samples/ControlCatalog/Pages/TextBoxPage.xaml.cs
  49. 7
      samples/IntegrationTestApp/App.axaml
  50. 24
      samples/IntegrationTestApp/App.axaml.cs
  51. 27
      samples/IntegrationTestApp/IntegrationTestApp.csproj
  52. 95
      samples/IntegrationTestApp/MainWindow.axaml
  53. 67
      samples/IntegrationTestApp/MainWindow.axaml.cs
  54. 22
      samples/IntegrationTestApp/Program.cs
  55. 5
      samples/IntegrationTestApp/bundle.sh
  56. 11
      samples/IntegrationTestApp/nuget.config
  57. 3
      samples/RenderDemo/MainWindow.xaml
  58. 71
      samples/RenderDemo/Pages/BrushesPage.axaml
  59. 18
      samples/RenderDemo/Pages/BrushesPage.axaml.cs
  60. 1
      samples/interop/WindowsInteropTest/WindowsInteropTest.csproj
  61. 27
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  62. 5
      src/Android/Avalonia.Android/AndroidPlatform.cs
  63. 9
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  64. 65
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  65. 7
      src/Android/Avalonia.Android/AvaloniaView.cs
  66. 11
      src/Android/Avalonia.Android/AvaloniaViewModel.cs
  67. 50
      src/Android/Avalonia.Android/Resources/AboutResources.txt
  68. 6
      src/Android/Avalonia.Android/Resources/Values/Strings.xml
  69. 4
      src/Android/Avalonia.Android/SoftKeyboardListener.cs
  70. 8
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  71. 2
      src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
  72. 33
      src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
  73. 10
      src/Avalonia.Base/Metadata/TrimSurroundingWhitespaceAttribute.cs
  74. 12
      src/Avalonia.Base/Metadata/WhitespaceSignificantCollectionAttribute.cs
  75. 3
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  76. 6
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  77. 1
      src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt
  78. 7
      src/Avalonia.Controls/ApiCompatBaseline.txt
  79. 28
      src/Avalonia.Controls/Automation/AutomationElementIdentifiers.cs
  80. 28
      src/Avalonia.Controls/Automation/AutomationLiveSetting.cs
  81. 630
      src/Avalonia.Controls/Automation/AutomationProperties.cs
  82. 11
      src/Avalonia.Controls/Automation/AutomationProperty.cs
  83. 21
      src/Avalonia.Controls/Automation/AutomationPropertyChangedEventArgs.cs
  84. 10
      src/Avalonia.Controls/Automation/ElementNotEnabledException.cs
  85. 15
      src/Avalonia.Controls/Automation/ExpandCollapsePatternIdentifiers.cs
  86. 29
      src/Avalonia.Controls/Automation/ExpandCollapseState.cs
  87. 26
      src/Avalonia.Controls/Automation/IsOffscreenBehavior.cs
  88. 263
      src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
  89. 43
      src/Avalonia.Controls/Automation/Peers/ButtonAutomationPeer.cs
  90. 137
      src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs
  91. 36
      src/Avalonia.Controls/Automation/Peers/ContentControlAutomationPeer.cs
  92. 221
      src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
  93. 54
      src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs
  94. 82
      src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs
  95. 59
      src/Avalonia.Controls/Automation/Peers/MenuItemAutomationPeer.cs
  96. 25
      src/Avalonia.Controls/Automation/Peers/NoneAutomationPeer.cs
  97. 48
      src/Avalonia.Controls/Automation/Peers/PopupAutomationPeer.cs
  98. 40
      src/Avalonia.Controls/Automation/Peers/PopupRootAutomationPeer.cs
  99. 34
      src/Avalonia.Controls/Automation/Peers/RangeBaseAutomationPeer.cs
  100. 172
      src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs

2
.editorconfig

@ -137,7 +137,7 @@ space_within_single_line_array_initializer_braces = true
csharp_wrap_before_ternary_opsigns = false
# Xaml files
[*.xaml]
[*.{xaml,axaml}]
indent_size = 2
# Xml project files

3
.gitignore

@ -192,13 +192,12 @@ dirs.sln
##################
# XCode
# Xcode
##################
Index/
Logs/
ModuleCache.noindex/
Build/Intermediates.noindex/
info.plist
build-intermediate
obj-Direct2D1/
obj-Skia/

5
.ncrunch/Avalonia.IntegrationTests.Appium.v3.ncrunchproject

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

5
.ncrunch/Avalonia.IntegrationTests.Win32.v3.ncrunchproject

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

5
.ncrunch/IntegrationTestApp.v3.ncrunchproject

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

132
Avalonia.sln

@ -95,8 +95,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog", "samples\C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.TestApp", "tests\Avalonia.DesignerSupport.TestApp\Avalonia.DesignerSupport.TestApp.csproj", "{F1381F98-4D24-409A-A6C5-1C5B1E08BB08}"
@ -121,7 +119,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\CoreLibraries.props = build\CoreLibraries.props
build\EmbedXaml.props = build\EmbedXaml.props
build\HarfBuzzSharp.props = build\HarfBuzzSharp.props
build\iOSWorkarounds.props = build\iOSWorkarounds.props
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props
@ -177,8 +174,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.OpenGL", "src\Aval
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Native", "src\Avalonia.Native\Avalonia.Native.csproj", "{12A91A62-C064-42CA-9A8C-A1272F354388}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesktopRuntime", "src\Avalonia.DesktopRuntime\Avalonia.DesktopRuntime.csproj", "{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Packages", "Packages", "{E870DCD7-F46A-498D-83FC-D0FD13E0A11C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia", "packages\Avalonia\Avalonia.csproj", "{D49233F8-F29C-47DD-9975-C4C9E4502720}"
@ -221,6 +216,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.MicroCom", "src\Av
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniMvvm", "samples\MiniMvvm\MiniMvvm.csproj", "{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTestApp", "samples\IntegrationTestApp\IntegrationTestApp.csproj", "{676D6BFD-029D-4E43-BFC7-3892265CE251}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.IntegrationTests.Appium", "tests\Avalonia.IntegrationTests.Appium\Avalonia.IntegrationTests.Appium.csproj", "{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web.Blazor", "src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj", "{25831348-EB2A-483E-9576-E8F6528674A5}"
@ -233,6 +232,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\S
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", "src\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj", "{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@ -1095,26 +1096,6 @@ Global
{2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhone.Build.0 = Release|Any CPU
{2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhone.Build.0 = AppStore|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.ActiveCfg = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.Build.0 = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|Any CPU.ActiveCfg = Release|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhone.ActiveCfg = Release|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhone.Build.0 = Release|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@ -1559,30 +1540,6 @@ Global
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.Build.0 = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhone.Build.0 = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|Any CPU.Build.0 = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhone.Build.0 = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|Any CPU.Build.0 = Release|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhone.ActiveCfg = Release|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhone.Build.0 = Release|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{878FEFE0-CD14-41CB-90B0-DBCB163E8F15}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@ -2063,6 +2020,54 @@ Global
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.Build.0 = Release|Any CPU
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhone.Build.0 = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|Any CPU.Build.0 = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhone.Build.0 = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|Any CPU.ActiveCfg = Release|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|Any CPU.Build.0 = Release|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhone.ActiveCfg = Release|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhone.Build.0 = Release|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhone.Build.0 = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhone.Build.0 = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.Build.0 = Release|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhone.ActiveCfg = Release|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhone.Build.0 = Release|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@ -2183,6 +2188,30 @@ Global
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhone.Build.0 = Release|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhone.Build.0 = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhone.Build.0 = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|Any CPU.Build.0 = Release|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhone.ActiveCfg = Release|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhone.Build.0 = Release|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2212,7 +2241,6 @@ Global
{410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{D0A739B9-3C68-4BA6-A328-41606954B6BD} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{2B888490-D14A-4BCA-AB4B-48676FA93C9B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{57E0455D-D565-44BB-B069-EE1AA20F8337} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{52F55355-D120-42AC-8116-8410A7D602FA} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{F1381F98-4D24-409A-A6C5-1C5B1E08BB08} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{FBCAF3D0-2808-4934-8E96-3F607594517B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
@ -2221,6 +2249,7 @@ Global
{29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} = {A689DEF5-D50F-4975-8B72-124C9EB54066}
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
@ -2240,10 +2269,13 @@ Global
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{676D6BFD-029D-4E43-BFC7-3892265CE251} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{25831348-EB2A-483E-9576-E8F6528674A5} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
{C08E9894-AA92-426E-BF56-033E262CAD3E} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{26A98DA1-D89D-4A95-8152-349F404DA2E2} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

8
azure-pipelines.yml

@ -38,7 +38,7 @@ jobs:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 6.0.100'
inputs:
version: 6.0.100
version: 6.0.200
- task: CmdLine@2
displayName: 'Run Build'
@ -69,7 +69,7 @@ jobs:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 6.0.100'
inputs:
version: 6.0.100
version: 6.0.200
- task: CmdLine@2
displayName: 'Install Mono 5.18'
@ -141,13 +141,13 @@ jobs:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 6.0.100'
inputs:
version: 6.0.100
version: 6.0.200
- task: CmdLine@2
displayName: 'Install Workloads'
inputs:
script: |
dotnet workload install android
dotnet workload install --no-cache --disable-parallel android ios --skip-manifest-update --source "https://api.nuget.org/v3/index.json"
- task: CmdLine@2
displayName: 'Install Nuke'

1
build/CoreLibraries.props

@ -16,7 +16,6 @@
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup/Avalonia.Markup.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.MicroCom/Avalonia.MicroCom.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj" Condition="'$(TargetFramework)' != 'netstandard2.0'" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj" />
</ItemGroup>
</Project>

6
build/HarfBuzzSharp.props

@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="HarfBuzzSharp" Version="2.8.2-preview.178" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2-preview.178" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2-preview.178"/>
<PackageReference Include="HarfBuzzSharp" Version="2.8.2-preview.209" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2-preview.209" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2-preview.209"/>
</ItemGroup>
</Project>

2
build/SharedVersion.props

@ -11,7 +11,7 @@
<LangVersion>latest</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon>
<PackageDescription>Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS.</PackageDescription>
<PackageDescription>Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS and with experimental support for Android, iOS and WebAssembly.</PackageDescription>
<PackageTags>avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin</PackageTags>
<PackageReleaseNotes>https://github.com/AvaloniaUI/Avalonia/releases</PackageReleaseNotes>
<RepositoryType>git</RepositoryType>

6
build/SkiaSharp.props

@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.88.0-preview.178" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0-preview.178" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0-preview.178"/>
<PackageReference Include="SkiaSharp" Version="2.88.0-preview.209" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0-preview.209" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0-preview.209"/>
</ItemGroup>
</Project>

18
build/XUnit.props

@ -1,14 +1,14 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="xunit" Version="2.3.0" />
<PackageReference Include="xunit.abstractions" Version="2.0.1" />
<PackageReference Include="xunit.assert" Version="2.3.0" />
<PackageReference Include="xunit.core" Version="2.3.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.3.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.3.0" />
<PackageReference Include="xunit.runner.console" Version="2.3.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0" />
<PackageReference Include="Xunit.SkippableFact" Version="1.3.6" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.assert" Version="2.4.1" />
<PackageReference Include="xunit.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageReference Include="xunit.runner.console" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="Xunit.SkippableFact" Version="1.4.13" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
</ItemGroup>
<PropertyGroup>

9
dirs.proj

@ -10,21 +10,20 @@
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/**/*.*proj" />
<ProjectReference Remove="tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj" />
<ProjectReference Remove="samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj" />
<ProjectReference Remove="samples/ControlCatalog.iOS.Legacy/ControlCatalog.iOS.Legacy.csproj" />
<ProjectReference Remove="samples/ControlCatalog.Android/ControlCatalog.Android.csproj" />
<ProjectReference Remove="src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj" />
</ItemGroup>
<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\iOS')">
<ProjectReference Remove="src/iOS/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj" />
</ItemGroup>
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows')) OR '$(MSBuildRuntimeType)' != 'Full'">
<ProjectReference Remove="src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj" />
<ProjectReference Remove="samples/interop/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Desktop/*.*proj" />
</ItemGroup>
<!-- Build android projects only on Windows, where we have installed android workload -->
<!-- Build android and iOS projects only on Windows, where we have installed android workload -->
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows'))">
<ProjectReference Remove="src/Android/**/*.*proj" />
<ProjectReference Remove="src/iOS/**/*.*proj" />
</ItemGroup>
<ItemGroup>

3
global.json

@ -1,6 +1,7 @@
{
"sdk": {
"version": "6.0.100"
"version": "6.0.200",
"rollForward": "latestFeature"
},
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43",

8
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@ -30,6 +30,8 @@
AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; };
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; };
BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; };
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -64,6 +66,8 @@
AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libAvalonia.Native.OSX.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = "<group>"; };
BC11A5BC2608D58F0017BAD0 /* automation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = automation.h; sourceTree = "<group>"; };
BC11A5BD2608D58F0017BAD0 /* automation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = automation.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -97,6 +101,8 @@
AB7A61E62147C814003C5833 = {
isa = PBXGroup;
children = (
BC11A5BC2608D58F0017BAD0 /* automation.h */,
BC11A5BD2608D58F0017BAD0 /* automation.mm */,
1A1852DB23E05814008F0DED /* deadlock.mm */,
1A002B9D232135EE00021753 /* app.mm */,
37DDA9B121933371002E132B /* AvnString.h */,
@ -143,6 +149,7 @@
buildActionMask = 2147483647;
files = (
37155CE4233C00EB0034DCE9 /* menu.h in Headers */,
BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -213,6 +220,7 @@
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */,
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */,
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */,
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,

1
native/Avalonia.Native/src/OSX/AvnString.h

@ -14,4 +14,5 @@ extern IAvnStringArray* CreateAvnStringArray(NSArray<NSString*>* array);
extern IAvnStringArray* CreateAvnStringArray(NSArray<NSURL*>* array);
extern IAvnStringArray* CreateAvnStringArray(NSString* string);
extern IAvnString* CreateByteArray(void* data, int len);
extern NSString* GetNSStringAndRelease(IAvnString* s);
#endif /* AvnString_h */

16
native/Avalonia.Native/src/OSX/AvnString.mm

@ -153,3 +153,19 @@ IAvnString* CreateByteArray(void* data, int len)
{
return new AvnStringImpl(data, len);
}
NSString* GetNSStringAndRelease(IAvnString* s)
{
NSString* result = nil;
if (s != nullptr)
{
char* p;
if (s->Pointer((void**)&p) == S_OK && p != nullptr)
result = [NSString stringWithUTF8String:p];
s->Release();
}
return result;
}

12
native/Avalonia.Native/src/OSX/automation.h

@ -0,0 +1,12 @@
#import <Cocoa/Cocoa.h>
#include "window.h"
NS_ASSUME_NONNULL_BEGIN
class IAvnAutomationPeer;
@interface AvnAccessibilityElement : NSAccessibilityElement
+ (AvnAccessibilityElement *) acquire:(IAvnAutomationPeer *) peer;
@end
NS_ASSUME_NONNULL_END

496
native/Avalonia.Native/src/OSX/automation.mm

@ -0,0 +1,496 @@
#include "common.h"
#include "automation.h"
#include "AvnString.h"
#include "window.h"
@interface AvnAccessibilityElement (Events)
- (void) raiseChildrenChanged;
@end
@interface AvnRootAccessibilityElement : AvnAccessibilityElement
- (AvnView *) ownerView;
- (AvnRootAccessibilityElement *) initWithPeer:(IAvnAutomationPeer *) peer owner:(AvnView*) owner;
- (void) raiseFocusChanged;
@end
class AutomationNode : public ComSingleObject<IAvnAutomationNode, &IID_IAvnAutomationNode>
{
public:
FORWARD_IUNKNOWN()
AutomationNode(AvnAccessibilityElement* owner)
{
_owner = owner;
}
AvnAccessibilityElement* GetOwner()
{
return _owner;
}
virtual void Dispose() override
{
_owner = nil;
}
virtual void ChildrenChanged () override
{
[_owner raiseChildrenChanged];
}
virtual void PropertyChanged (AvnAutomationProperty property) override
{
}
virtual void FocusChanged () override
{
[(AvnRootAccessibilityElement*)_owner raiseFocusChanged];
}
private:
__strong AvnAccessibilityElement* _owner;
};
@implementation AvnAccessibilityElement
{
IAvnAutomationPeer* _peer;
AutomationNode* _node;
NSMutableArray* _children;
}
+ (AvnAccessibilityElement *)acquire:(IAvnAutomationPeer *)peer
{
if (peer == nullptr)
return nil;
auto instance = peer->GetNode();
if (instance != nullptr)
return dynamic_cast<AutomationNode*>(instance)->GetOwner();
if (peer->IsRootProvider())
{
auto window = peer->RootProvider_GetWindow();
auto holder = dynamic_cast<INSWindowHolder*>(window);
auto view = holder->GetNSView();
return [[AvnRootAccessibilityElement alloc] initWithPeer:peer owner:view];
}
else
{
return [[AvnAccessibilityElement alloc] initWithPeer:peer];
}
}
- (AvnAccessibilityElement *)initWithPeer:(IAvnAutomationPeer *)peer
{
self = [super init];
_peer = peer;
_node = new AutomationNode(self);
_peer->SetNode(_node);
return self;
}
- (void)dealloc
{
if (_node)
delete _node;
_node = nullptr;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ '%@' (%p)",
GetNSStringAndRelease(_peer->GetClassName()),
GetNSStringAndRelease(_peer->GetName()),
_peer];
}
- (IAvnAutomationPeer *)peer
{
return _peer;
}
- (BOOL)isAccessibilityElement
{
return _peer->IsControlElement();
}
- (NSAccessibilityRole)accessibilityRole
{
auto controlType = _peer->GetAutomationControlType();
switch (controlType) {
case AutomationButton: return NSAccessibilityButtonRole;
case AutomationCalendar: return NSAccessibilityGridRole;
case AutomationCheckBox: return NSAccessibilityCheckBoxRole;
case AutomationComboBox: return NSAccessibilityPopUpButtonRole;
case AutomationComboBoxItem: return NSAccessibilityMenuItemRole;
case AutomationEdit: return NSAccessibilityTextFieldRole;
case AutomationHyperlink: return NSAccessibilityLinkRole;
case AutomationImage: return NSAccessibilityImageRole;
case AutomationListItem: return NSAccessibilityRowRole;
case AutomationList: return NSAccessibilityTableRole;
case AutomationMenu: return NSAccessibilityMenuBarRole;
case AutomationMenuBar: return NSAccessibilityMenuBarRole;
case AutomationMenuItem: return NSAccessibilityMenuItemRole;
case AutomationProgressBar: return NSAccessibilityProgressIndicatorRole;
case AutomationRadioButton: return NSAccessibilityRadioButtonRole;
case AutomationScrollBar: return NSAccessibilityScrollBarRole;
case AutomationSlider: return NSAccessibilitySliderRole;
case AutomationSpinner: return NSAccessibilityIncrementorRole;
case AutomationStatusBar: return NSAccessibilityTableRole;
case AutomationTab: return NSAccessibilityTabGroupRole;
case AutomationTabItem: return NSAccessibilityRadioButtonRole;
case AutomationText: return NSAccessibilityStaticTextRole;
case AutomationToolBar: return NSAccessibilityToolbarRole;
case AutomationToolTip: return NSAccessibilityPopoverRole;
case AutomationTree: return NSAccessibilityOutlineRole;
case AutomationTreeItem: return NSAccessibilityCellRole;
case AutomationCustom: return NSAccessibilityUnknownRole;
case AutomationGroup: return NSAccessibilityGroupRole;
case AutomationThumb: return NSAccessibilityHandleRole;
case AutomationDataGrid: return NSAccessibilityGridRole;
case AutomationDataItem: return NSAccessibilityCellRole;
case AutomationDocument: return NSAccessibilityStaticTextRole;
case AutomationSplitButton: return NSAccessibilityPopUpButtonRole;
case AutomationWindow: return NSAccessibilityWindowRole;
case AutomationPane: return NSAccessibilityGroupRole;
case AutomationHeader: return NSAccessibilityGroupRole;
case AutomationHeaderItem: return NSAccessibilityButtonRole;
case AutomationTable: return NSAccessibilityTableRole;
case AutomationTitleBar: return NSAccessibilityGroupRole;
// Treat unknown roles as generic group container items. Returning
// NSAccessibilityUnknownRole is also possible but makes the screen
// reader focus on the item instead of passing focus to child items.
default: return NSAccessibilityGroupRole;
}
}
- (NSString *)accessibilityIdentifier
{
return GetNSStringAndRelease(_peer->GetAutomationId());
}
- (NSString *)accessibilityTitle
{
// StaticText exposes its text via the value property.
if (_peer->GetAutomationControlType() != AutomationText)
{
return GetNSStringAndRelease(_peer->GetName());
}
return [super accessibilityTitle];
}
- (id)accessibilityValue
{
if (_peer->IsRangeValueProvider())
{
return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetValue()];
}
else if (_peer->IsToggleProvider())
{
switch (_peer->ToggleProvider_GetToggleState()) {
case 0: return [NSNumber numberWithBool:NO];
case 1: return [NSNumber numberWithBool:YES];
default: return [NSNumber numberWithInt:2];
}
}
else if (_peer->IsValueProvider())
{
return GetNSStringAndRelease(_peer->ValueProvider_GetValue());
}
else if (_peer->GetAutomationControlType() == AutomationText)
{
return GetNSStringAndRelease(_peer->GetName());
}
return [super accessibilityValue];
}
- (id)accessibilityMinValue
{
if (_peer->IsRangeValueProvider())
{
return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetMinimum()];
}
return [super accessibilityMinValue];
}
- (id)accessibilityMaxValue
{
if (_peer->IsRangeValueProvider())
{
return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetMaximum()];
}
return [super accessibilityMaxValue];
}
- (BOOL)isAccessibilityEnabled
{
return _peer->IsEnabled();
}
- (BOOL)isAccessibilityFocused
{
return _peer->HasKeyboardFocus();
}
- (NSArray *)accessibilityChildren
{
if (_children == nullptr && _peer != nullptr)
[self recalculateChildren];
return _children;
}
- (NSRect)accessibilityFrame
{
id topLevel = [self accessibilityTopLevelUIElement];
auto result = NSZeroRect;
if ([topLevel isKindOfClass:[AvnRootAccessibilityElement class]])
{
auto root = (AvnRootAccessibilityElement*)topLevel;
auto view = [root ownerView];
if (view)
{
auto window = [view window];
auto bounds = ToNSRect(_peer->GetBoundingRectangle());
auto windowBounds = [view convertRect:bounds toView:nil];
auto screenBounds = [window convertRectToScreen:windowBounds];
result = screenBounds;
}
}
return result;
}
- (id)accessibilityParent
{
auto parentPeer = _peer->GetParent();
return parentPeer ? [AvnAccessibilityElement acquire:parentPeer] : [NSApplication sharedApplication];
}
- (id)accessibilityTopLevelUIElement
{
auto rootPeer = _peer->GetRootPeer();
return [AvnAccessibilityElement acquire:rootPeer];
}
- (id)accessibilityWindow
{
id topLevel = [self accessibilityTopLevelUIElement];
return [topLevel isKindOfClass:[NSWindow class]] ? topLevel : nil;
}
- (BOOL)isAccessibilityExpanded
{
if (!_peer->IsExpandCollapseProvider())
return NO;
return _peer->ExpandCollapseProvider_GetIsExpanded();
}
- (void)setAccessibilityExpanded:(BOOL)accessibilityExpanded
{
if (!_peer->IsExpandCollapseProvider())
return;
if (accessibilityExpanded)
_peer->ExpandCollapseProvider_Expand();
else
_peer->ExpandCollapseProvider_Collapse();
}
- (BOOL)accessibilityPerformPress
{
if (_peer->IsInvokeProvider())
{
_peer->InvokeProvider_Invoke();
}
else if (_peer->IsExpandCollapseProvider())
{
_peer->ExpandCollapseProvider_Expand();
}
else if (_peer->IsToggleProvider())
{
_peer->ToggleProvider_Toggle();
}
return YES;
}
- (BOOL)accessibilityPerformIncrement
{
if (!_peer->IsRangeValueProvider())
return NO;
auto value = _peer->RangeValueProvider_GetValue();
value += _peer->RangeValueProvider_GetSmallChange();
_peer->RangeValueProvider_SetValue(value);
return YES;
}
- (BOOL)accessibilityPerformDecrement
{
if (!_peer->IsRangeValueProvider())
return NO;
auto value = _peer->RangeValueProvider_GetValue();
value -= _peer->RangeValueProvider_GetSmallChange();
_peer->RangeValueProvider_SetValue(value);
return YES;
}
- (BOOL)accessibilityPerformShowMenu
{
if (!_peer->IsExpandCollapseProvider())
return NO;
_peer->ExpandCollapseProvider_Expand();
return YES;
}
- (BOOL)isAccessibilitySelected
{
if (_peer->IsSelectionItemProvider())
return _peer->SelectionItemProvider_IsSelected();
return NO;
}
- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector
{
if (selector == @selector(accessibilityPerformShowMenu))
{
return _peer->IsExpandCollapseProvider() && _peer->ExpandCollapseProvider_GetShowsMenu();
}
else if (selector == @selector(isAccessibilityExpanded))
{
return _peer->IsExpandCollapseProvider();
}
else if (selector == @selector(accessibilityPerformPress))
{
return _peer->IsInvokeProvider() || _peer->IsExpandCollapseProvider() || _peer->IsToggleProvider();
}
else if (selector == @selector(accessibilityPerformIncrement) ||
selector == @selector(accessibilityPerformDecrement) ||
selector == @selector(accessibilityMinValue) ||
selector == @selector(accessibilityMaxValue))
{
return _peer->IsRangeValueProvider();
}
return [super isAccessibilitySelectorAllowed:selector];
}
- (void)raiseChildrenChanged
{
auto changed = _children ? [NSMutableSet setWithArray:_children] : [NSMutableSet set];
[self recalculateChildren];
if (_children)
[changed addObjectsFromArray:_children];
NSAccessibilityPostNotificationWithUserInfo(
self,
NSAccessibilityLayoutChangedNotification,
@{ NSAccessibilityUIElementsKey: [changed allObjects]});
}
- (void)raisePropertyChanged
{
}
- (void)setAccessibilityFocused:(BOOL)accessibilityFocused
{
if (accessibilityFocused)
_peer->SetFocus();
}
- (void)recalculateChildren
{
auto childPeers = _peer->GetChildren();
auto childCount = childPeers != nullptr ? childPeers->GetCount() : 0;
if (childCount > 0)
{
_children = [[NSMutableArray alloc] initWithCapacity:childCount];
for (int i = 0; i < childCount; ++i)
{
IAvnAutomationPeer* child;
if (childPeers->Get(i, &child) == S_OK)
{
auto element = [AvnAccessibilityElement acquire:child];
[_children addObject:element];
}
}
}
else
{
_children = nil;
}
}
@end
@implementation AvnRootAccessibilityElement
{
AvnView* _owner;
}
- (AvnRootAccessibilityElement *)initWithPeer:(IAvnAutomationPeer *)peer owner:(AvnView *)owner
{
self = [super initWithPeer:peer];
_owner = owner;
// Seems we need to raise a focus changed notification here if we have focus
auto focusedPeer = [self peer]->RootProvider_GetFocus();
id focused = [AvnAccessibilityElement acquire:focusedPeer];
if (focused)
NSAccessibilityPostNotification(focused, NSAccessibilityFocusedUIElementChangedNotification);
return self;
}
- (AvnView *)ownerView
{
return _owner;
}
- (id)accessibilityFocusedUIElement
{
auto focusedPeer = [self peer]->RootProvider_GetFocus();
return [AvnAccessibilityElement acquire:focusedPeer];
}
- (id)accessibilityHitTest:(NSPoint)point
{
auto clientPoint = [[_owner window] convertPointFromScreen:point];
auto localPoint = [_owner translateLocalPoint:ToAvnPoint(clientPoint)];
auto hit = [self peer]->RootProvider_GetPeerFromPoint(localPoint);
return [AvnAccessibilityElement acquire:hit];
}
- (id)accessibilityParent
{
return _owner;
}
- (void)raiseFocusChanged
{
id focused = [self accessibilityFocusedUIElement];
NSAccessibilityPostNotification(focused, NSAccessibilityFocusedUIElementChangedNotification);
}
// Although this method is marked as deprecated we get runtime warnings if we don't handle it.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
- (void)accessibilityPerformAction:(NSAccessibilityActionName)action
{
[_owner accessibilityPerformAction:action];
}
#pragma clang diagnostic pop
@end

1
native/Avalonia.Native/src/OSX/common.h

@ -35,6 +35,7 @@ extern NSMenuItem* GetAppMenuItem ();
extern void InitializeAvnApp(IAvnApplicationEvents* events);
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy;
extern NSPoint ToNSPoint (AvnPoint p);
extern NSRect ToNSRect (AvnRect r);
extern AvnPoint ToAvnPoint (NSPoint p);
extern AvnPoint ConvertPointY (AvnPoint p);
extern CGFloat PrimaryDisplayHeight();

12
native/Avalonia.Native/src/OSX/main.mm

@ -1,6 +1,7 @@
//This file will contain actual IID structures
#define COM_GUIDS_MATERIALIZE
#include "common.h"
#include "window.h"
static NSString* s_appTitle = @"Avalonia";
@ -335,7 +336,7 @@ public:
return S_OK;
}
}
virtual HRESULT SetAppMenu (IAvnMenu* appMenu) override
{
START_COM_CALL;
@ -400,6 +401,15 @@ NSPoint ToNSPoint (AvnPoint p)
return result;
}
NSRect ToNSRect (AvnRect r)
{
return NSRect
{
NSPoint { r.X, r.Y },
NSSize { r.Width, r.Height }
};
}
AvnPoint ToAvnPoint (NSPoint p)
{
AvnPoint result;

1
native/Avalonia.Native/src/OSX/window.h

@ -43,6 +43,7 @@ class WindowBaseImpl;
struct INSWindowHolder
{
virtual AvnWindow* _Nonnull GetNSWindow () = 0;
virtual AvnView* _Nonnull GetNSView () = 0;
};
struct IWindowStateChanged

54
native/Avalonia.Native/src/OSX/window.mm

@ -5,14 +5,22 @@
#include "menu.h"
#include <OpenGL/gl.h>
#include "rendertarget.h"
#include "AvnString.h"
#include "automation.h"
class WindowBaseImpl : public virtual ComSingleObject<IAvnWindowBase, &IID_IAvnWindowBase>, public INSWindowHolder
class WindowBaseImpl : public virtual ComObject,
public virtual IAvnWindowBase,
public INSWindowHolder
{
private:
NSCursor* cursor;
public:
FORWARD_IUNKNOWN()
BEGIN_INTERFACE_MAP()
INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase)
END_INTERFACE_MAP()
virtual ~WindowBaseImpl()
{
View = NULL;
@ -115,7 +123,12 @@ public:
{
return Window;
}
virtual AvnView* GetNSView() override
{
return View;
}
virtual HRESULT Show(bool activate, bool isDialog) override
{
START_COM_CALL;
@ -722,7 +735,7 @@ private:
return E_INVALIDARG;
// If one tries to show a child window with a minimized parent window, then the parent window will be
// restored but MacOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
// restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive
// state. Detect this and explicitly restore the parent window ourselves to avoid this situation.
if (cparent->WindowState() == Minimized)
cparent->SetWindowState(Normal);
@ -1396,6 +1409,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
AvnPixelSize _lastPixelSize;
NSObject<IRenderTarget>* _renderTarget;
AvnPlatformResizeReason _resizeReason;
AvnAccessibilityElement* _accessibilityChild;
}
- (void)onClosed
@ -2050,6 +2064,37 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_resizeReason = reason;
}
- (AvnAccessibilityElement *) accessibilityChild
{
if (_accessibilityChild == nil)
{
auto peer = _parent->BaseEvents->GetAutomationPeer();
if (peer == nil)
return nil;
_accessibilityChild = [AvnAccessibilityElement acquire:peer];
}
return _accessibilityChild;
}
- (NSArray *)accessibilityChildren
{
auto child = [self accessibilityChild];
return NSAccessibilityUnignoredChildrenForOnlyChild(child);
}
- (id)accessibilityHitTest:(NSPoint)point
{
return [[self accessibilityChild] accessibilityHitTest:point];
}
- (id)accessibilityFocusedUIElement
{
return [[self accessibilityChild] accessibilityFocusedUIElement];
}
@end
@ -2062,6 +2107,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
bool _isExtended;
AvnMenu* _menu;
double _lastScaling;
IAvnAutomationPeer* _automationPeer;
NSMutableArray* _automationChildren;
}
-(void) setIsExtended:(bool)value;
@ -2465,6 +2512,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
}
}
}
@end
class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup

5
nukebuild/numerge.config

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

2
readme.md

@ -5,7 +5,7 @@
## 📖 About
Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOs. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM.
Avalonia 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.
![image](https://user-images.githubusercontent.com/4672627/152126443-932966cf-57e7-4e77-9be6-62463a66b9f8.png)

16
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -21,9 +21,23 @@
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
<DebugSymbols>True</DebugSymbols>
<RunAOTCompilation>True</RunAOTCompilation>
<RunAOTCompilation>False</RunAOTCompilation>
<EnableLLVM>True</EnableLLVM>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
<RunAOTCompilation>False</RunAOTCompilation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj" />

13
samples/ControlCatalog.Android/MainActivity.cs

@ -1,19 +1,16 @@
using Android.App;
using Android.OS;
using Android.Content.PM;
using Avalonia;
using Avalonia.Android;
namespace ControlCatalog.Android
{
[Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)]
public class MainActivity : AvaloniaActivity
[Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
public class MainActivity : AvaloniaActivity<App>
{
protected override void OnCreate(Bundle? savedInstanceState)
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
{
base.OnCreate(savedInstanceState);
Content = new MainView();
return base.CustomizeAppBuilder(builder);
}
}
}

2
samples/ControlCatalog.Android/Resources/values/styles.xml

@ -4,7 +4,7 @@
<style name="MyTheme">
</style>
<style name="MyTheme.NoActionBar">
<style name="MyTheme.NoActionBar" parent="@style/Theme.AppCompat.NoActionBar">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>

10
samples/ControlCatalog.Android/SplashActivity.cs

@ -1,9 +1,6 @@
using Android.App;
using Android.Content;
using Android.OS;
using Application = Android.App.Application;
using Avalonia;
namespace ControlCatalog.Android
{
@ -19,13 +16,6 @@ namespace ControlCatalog.Android
{
base.OnResume();
if (Avalonia.Application.Current == null)
{
AppBuilder.Configure<App>()
.UseAndroid()
.SetupWithoutStarting();
}
StartActivity(new Intent(Application.Context, typeof(MainActivity)));
}
}

15
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -6,7 +6,14 @@
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup>
<PropertyGroup Condition="'$(RunAotCompilation)' == 'true'">
<IlcTrimMetadata>true</IlcTrimMetadata>
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json</RestoreAdditionalProjectSources>
<NativeAotCompilerVersion>7.0.0-*</NativeAotCompilerVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
@ -15,6 +22,14 @@
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
</ItemGroup>
<ItemGroup Condition="'$(RunAotCompilation)' == 'true'">
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="$(NativeAotCompilerVersion)" />
<!-- Cross-compilation for Windows x64-arm64 and Linux x64-arm64 -->
<PackageReference Condition="'$(RuntimeIdentifier)'=='win-arm64'" Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="$(NativeAotCompilerVersion)" />
<PackageReference Condition="'$(RuntimeIdentifier)'=='linux-arm64'" Include="runtime.linux-x64.Microsoft.DotNet.ILCompiler" Version="$(NativeAotCompilerVersion)" />
<RdXmlFile Include="rd.xml" />
</ItemGroup>
<PropertyGroup>
<!-- For Microsoft.CodeAnalysis -->
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>

7
samples/ControlCatalog.NetCore/Program.cs

@ -118,6 +118,13 @@ namespace ControlCatalog.NetCore
})
.UseSkia()
.UseManagedSystemDialogs()
.AfterSetup(builder =>
{
builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions()
{
StartupScreenIndex = 1,
});
})
.LogToTrace();
static void SilenceConsole()

7
samples/ControlCatalog.NetCore/rd.xml

@ -0,0 +1,7 @@
<Directives>
<Application>
<Assembly Name="ControlCatalog" Dynamic="Required All"></Assembly>
<Assembly Name="Avalonia.Themes.Default" Dynamic="Required All"></Assembly>
<Assembly Name="Avalonia.Themes.Fluent" Dynamic="Required All"></Assembly>
</Application>
</Directives>

190
samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj

@ -1,186 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{57E0455D-D565-44BB-B069-EE1AA20F8337}</ProjectGuid>
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Exe</OutputType>
<RootNamespace>ControlCatalog.iOS</RootNamespace>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<AssemblyName>ControlCatalogiOS</AssemblyName>
<MtouchEnableSGenConc>true</MtouchEnableSGenConc>
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<ProvisioningType>automatic</ProvisioningType>
<ProvisioningType>manual</ProvisioningType>
<TargetFramework>net6.0-ios</TargetFramework>
<SupportedOSPlatformVersion>10.0</SupportedOSPlatformVersion>
<!-- temporal workaround for our GL interface backend -->
<UseInterpreter>True</UseInterpreter>
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
<!-- <RuntimeIdentifier>ios-arm64</RuntimeIdentifier>-->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>x86_64</MtouchArch>
<MtouchLink>None</MtouchLink>
<MtouchDebug>True</MtouchDebug>
<MtouchSdkVersion>9.1</MtouchSdkVersion>
<MtouchProfiling>False</MtouchProfiling>
<MtouchFastDev>False</MtouchFastDev>
<MtouchNoSymbolStrip>False</MtouchNoSymbolStrip>
<MtouchUseLlvm>False</MtouchUseLlvm>
<MtouchUseThumb>False</MtouchUseThumb>
<MtouchEnableBitcode>False</MtouchEnableBitcode>
<MtouchUseSGen>False</MtouchUseSGen>
<MtouchUseRefCounting>False</MtouchUseRefCounting>
<OptimizePNGs>True</OptimizePNGs>
<MtouchTlsProvider>Default</MtouchTlsProvider>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchFloat32>False</MtouchFloat32>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MtouchLink>None</MtouchLink>
<MtouchArch>x86_64</MtouchArch>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhone\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>true</MtouchDebug>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhone\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<ConsolePause>false</ConsolePause>
<CodesignKey>iPhone Developer</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Ad-Hoc|iPhone' ">
<DebugType>none</DebugType>
<Optimize>True</Optimize>
<OutputPath>bin\iPhone\Ad-Hoc</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<BuildIpa>True</BuildIpa>
<CodesignProvision>Automatic:AdHoc</CodesignProvision>
<CodesignKey>iPhone Distribution</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'AppStore|iPhone' ">
<DebugType>none</DebugType>
<Optimize>True</Optimize>
<OutputPath>bin\iPhone\AppStore</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<CodesignProvision>Automatic:AppStore</CodesignProvision>
<CodesignKey>iPhone Distribution</CodesignKey>
</PropertyGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<None Include="Info.plist" />
<Compile Include="Properties\AssemblyInfo.cs" />
<InterfaceDefinition Include="Resources\LaunchScreen.xib" />
<Content Include="Entitlements.plist" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Xamarin.iOS" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\iOS\Avalonia.iOS\Avalonia.iOS.csproj">
<Project>{4488AD85-1495-4809-9AA4-DDFE0A48527E}</Project>
<Name>Avalonia.iOS</Name>
<IsAppExtension>false</IsAppExtension>
<IsWatchApp>false</IsWatchApp>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3E53A01A-B331-47F3-B828-4A5717E77A24}</Project>
<Name>Avalonia.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj">
<Project>{6417E941-21BC-467B-A771-0DE389353CE6}</Project>
<Name>Avalonia.Markup</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{D211E587-D8BC-45B9-95A4-F297C8FA5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj">
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
<Project>{7062AE20-5DCC-4442-9645-8195BDECE63E}</Project>
<Name>Avalonia.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024B2D-53EB-4638-B26B-85EEAA54866E}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4C81-8AFF-9F27B8E31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{EB582467-6ABB-43A1-B052-E981BA910E3A}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
<Project>{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}</Project>
<Name>Avalonia.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj">
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project>
<Name>Avalonia.Skia</Name>
</ProjectReference>
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj">
<Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project>
<Name>ControlCatalog</Name>
</ProjectReference>
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.2-preview.33" />
<ProjectReference Include="..\..\src\iOS\Avalonia.iOS\Avalonia.iOS.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Import Project="..\..\build\LegacyProject.targets" />
<Import Project="..\..\build\SkiaSharp.props" />
<Import Project="..\..\build\HarfBuzzSharp.props" />
</Project>

9
samples/ControlCatalog.iOS/Info.plist

@ -5,7 +5,7 @@
<key>CFBundleDisplayName</key>
<string>ControlCatalog.iOS</string>
<key>CFBundleIdentifier</key>
<string>com.companyname.ControlCatalog.iOS</string>
<string>Avalonia.ControlCatalog</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>8.0</string>
<string>10.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
@ -28,6 +28,7 @@
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
@ -38,5 +39,9 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIStatusBarHidden</key>
<true/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

4
samples/ControlCatalog.iOS/Main.cs

@ -9,7 +9,7 @@ namespace ControlCatalog.iOS
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, "AppDelegate");
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}
}

36
samples/ControlCatalog.iOS/Properties/AssemblyInfo.cs

@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ControlCatalog.iOS")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ControlCatalog.iOS")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("57e0455d-d565-44bb-b069-ee1aa20f8337")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

2
samples/ControlCatalog.iOS/Resources/LaunchScreen.xib

@ -11,7 +11,7 @@
<rect key="frame" x="0.0" y="0.0" width="480" height="480" />
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" />
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2016 " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines"
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2022 " textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines"
minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21" />
<fontDescription key="fontDescription" type="system" pointSize="17" />

5
samples/ControlCatalog/App.xaml.cs

@ -78,11 +78,6 @@ namespace ControlCatalog
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
{
desktopLifetime.MainWindow = new MainWindow();
this.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions()
{
StartupScreenIndex = 1,
});
}
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime)
{

1
samples/ControlCatalog/ControlCatalog.csproj

@ -23,7 +23,6 @@
<ItemGroup>
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
<ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />
<ProjectReference Include="..\SampleControls\ControlSamples.csproj" />

3
samples/ControlCatalog/MainView.xaml

@ -40,6 +40,9 @@
<TabItem Header="CheckBox">
<pages:CheckBoxPage />
</TabItem>
<TabItem Header="Clipboard">
<pages:ClipboardPage />
</TabItem>
<TabItem Header="ComboBox">
<pages:ComboBoxPage />
</TabItem>

23
samples/ControlCatalog/Pages/ClipboardPage.xaml

@ -0,0 +1,23 @@
<UserControl x:Class="ControlCatalog.Pages.ClipboardPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">Example of ClipboardPage capabilities</TextBlock>
<Button Click="CopyText" Content="Copy text to clipboard" />
<Button Click="PasteText" Content="Paste text from clipboard" />
<Button Click="CopyTextDataObject" Content="Copy text to clipboard (data object)" />
<Button Click="PasteTextDataObject" Content="Paste text from clipboard (data object)" />
<Button Click="CopyFilesDataObject" Content="Copy files to clipboard (data object)" />
<Button Click="PasteFilesDataObject" Content="Paste files from clipboard (data object)" />
<Button Click="GetFormats" Content="Get clipboard formats" />
<Button Click="Clear" Content="Clear clipboard" />
<TextBox x:Name="ClipboardContent"
MinHeight="100"
AcceptsReturn="True"
Watermark="Text to copy of file names per line" />
</StackPanel>
</UserControl>

77
samples/ControlCatalog/Pages/ClipboardPage.xaml.cs

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public partial class ClipboardPage : UserControl
{
public ClipboardPage()
{
InitializeComponent();
}
private TextBox ClipboardContent => this.Get<TextBox>("ClipboardContent");
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private async void CopyText(object sender, RoutedEventArgs args)
{
await Application.Current.Clipboard.SetTextAsync(ClipboardContent.Text);
}
private async void PasteText(object sender, RoutedEventArgs args)
{
ClipboardContent.Text = await Application.Current.Clipboard.GetTextAsync();
}
private async void CopyTextDataObject(object sender, RoutedEventArgs args)
{
var dataObject = new DataObject();
dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
}
private async void PasteTextDataObject(object sender, RoutedEventArgs args)
{
ClipboardContent.Text = await Application.Current.Clipboard.GetDataAsync(DataFormats.Text) as string ?? string.Empty;
}
private async void CopyFilesDataObject(object sender, RoutedEventArgs args)
{
var files = ClipboardContent.Text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
if (files.Length == 0)
{
return;
}
var dataObject = new DataObject();
dataObject.Set(DataFormats.FileNames, files);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
}
private async void PasteFilesDataObject(object sender, RoutedEventArgs args)
{
var fiels = await Application.Current.Clipboard.GetDataAsync(DataFormats.FileNames) as IEnumerable<string>;
ClipboardContent.Text = fiels != null ? string.Join(Environment.NewLine, fiels) : string.Empty;
}
private async void GetFormats(object sender, RoutedEventArgs args)
{
var formats = await Application.Current.Clipboard.GetFormatsAsync();
ClipboardContent.Text = string.Join(Environment.NewLine, formats);
}
private async void Clear(object sender, RoutedEventArgs args)
{
await Application.Current.Clipboard.ClearAsync();
}
}
}

8
samples/ControlCatalog/Pages/DragAndDropPage.xaml

@ -11,7 +11,13 @@
Padding="16"
BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="2">
<TextBlock Name="DragStateText" TextWrapping="Wrap">Drag Me</TextBlock>
<TextBlock Name="DragStateText" TextWrapping="Wrap">Drag Me (text)</TextBlock>
</Border>
<Border Name="DragMeFiles"
Padding="16"
BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="2">
<TextBlock Name="DragStateFiles" TextWrapping="Wrap">Drag Me (files)</TextBlock>
</Border>
<Border Name="DragMeCustom"
Padding="16"

4
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@ -2,11 +2,8 @@
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace ControlCatalog.Pages
{
@ -24,6 +21,7 @@ namespace ControlCatalog.Pages
$"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"), DragDropEffects.Move);
SetupDnd("Files", d => d.Set(DataFormats.FileNames, new[] { Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName }), DragDropEffects.Copy);
}
void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)

2
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@ -111,7 +111,7 @@ namespace ControlCatalog.Pages
private void ScrollTo(int index)
{
System.Diagnostics.Debug.WriteLine("Scroll to " + index);
var layoutManager = ((Window)this.GetVisualRoot()).LayoutManager;
var layoutManager = ((TopLevel)VisualRoot).LayoutManager;
var element = _repeater.GetOrCreateElement(index);
layoutManager.ExecuteLayoutPass();
element.BringIntoView();

11
samples/ControlCatalog/Pages/TextBlockPage.xaml

@ -117,6 +117,17 @@
<TextBlock Text="👪 👨‍👩‍👧 👨‍👩‍👧‍👦" />
</StackPanel>
</Border>
<Border>
<TextBlock Margin="10" TextWrapping="Wrap">
This <Span FontWeight="Bold">is</Span> a
<Span Background="Silver" Foreground="Maroon">TextBlock</Span>
with <Span TextDecorations="Underline">several</Span>
<Span FontStyle="Italic">Span</Span> elements,
<Span Foreground="Blue">
using a <Bold>variety</Bold> of <Italic>styles</Italic>
</Span>.
</TextBlock>
</Border>
</WrapPanel>
</StackPanel>
</UserControl>

3
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -18,7 +18,7 @@
</TextBox.ContextFlyout>
</TextBox>
<TextBox Width="200" Watermark="ReadOnly" IsReadOnly="True" Text="This is read only"/>
<TextBox Width="200" Watermark="Numeric with watermark" x:Name="numericWatermark"/>
<TextBox Width="200" Watermark="Numeric with watermark" TextInputOptions.ContentType="Number" />
<TextBox Width="200"
Watermark="Floating Watermark"
UseFloatingWatermark="True"
@ -34,6 +34,7 @@
<TextBox Width="200"
Watermark="Password Box"
Classes="revealPasswordButton"
TextInputOptions.ContentType="Password"
UseFloatingWatermark="True"
PasswordChar="*"
Text="Password" />

6
samples/ControlCatalog/Pages/TextBoxPage.xaml.cs

@ -13,12 +13,6 @@ namespace ControlCatalog.Pages
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
this.Get<TextBox>("numericWatermark")
.TextInputOptionsQuery += (s, a) =>
{
a.ContentType = Avalonia.Input.TextInput.TextInputContentType.Number;
};
}
}
}

7
samples/IntegrationTestApp/App.axaml

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

24
samples/IntegrationTestApp/App.axaml.cs

@ -0,0 +1,24 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace IntegrationTestApp
{
public class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
}
base.OnFrameworkInitializationCompleted();
}
}
}

27
samples/IntegrationTestApp/IntegrationTestApp.csproj

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<CFBundleName>IntegrationTestApp</CFBundleName>
<CFBundleIdentifier>net.avaloniaui.avalonia.integrationtestapp</CFBundleIdentifier>
<NSHighResolutionCapable>true</NSHighResolutionCapable>
<CFBundleShortVersionString>1.0.0</CFBundleShortVersionString>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dotnet.Bundle" Version="0.9.13" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
</ItemGroup>
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\ReferenceCoreLibraries.props" />
</Project>

95
samples/IntegrationTestApp/MainWindow.axaml

@ -0,0 +1,95 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="IntegrationTestApp.MainWindow"
Title="IntegrationTestApp">
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="File">
<NativeMenu>
<NativeMenuItem Header="Open..."/>
</NativeMenu>
</NativeMenuItem>
<NativeMenuItem Header="View">
<NativeMenu/>
</NativeMenuItem>
</NativeMenu>
</NativeMenu.Menu>
<DockPanel>
<NativeMenuBar DockPanel.Dock="Top"/>
<TabControl TabStripPlacement="Left" Name="MainTabs">
<TabItem Header="Automation">
<StackPanel>
<TextBlock Name="TextBlockWithName">TextBlockWithName</TextBlock>
<TextBlock Name="NotTheAutomationId" AutomationProperties.AutomationId="TextBlockWithNameAndAutomationId">
TextBlockWithNameAndAutomationId
</TextBlock>
<TextBlock Name="TextBlockAsLabel">Label for TextBox</TextBlock>
<TextBox Name="LabeledByTextBox" AutomationProperties.LabeledBy="{Binding #TextBlockAsLabel}">
Foo
</TextBox>
</StackPanel>
</TabItem>
<TabItem Header="Button">
<StackPanel>
<Button Name="DisabledButton" IsEnabled="False">
Disabled Button
</Button>
<Button Name="BasicButton">
Basic Button
</Button>
<Button Name="ButtonWithTextBlock">
<TextBlock>Button with TextBlock</TextBlock>
</Button>
<Button Name="ButtonWithAcceleratorKey" HotKey="Ctrl+B">Button with Accelerator Key</Button>
</StackPanel>
</TabItem>
<TabItem Header="CheckBox">
<StackPanel>
<CheckBox Name="UncheckedCheckBox">Unchecked</CheckBox>
<CheckBox Name="CheckedCheckBox" IsChecked="True">Checked</CheckBox>
<CheckBox Name="ThreeStateCheckBox" IsThreeState="True" IsChecked="{x:Null}">ThreeState</CheckBox>
</StackPanel>
</TabItem>
<TabItem Header="ComboBox">
<StackPanel>
<ComboBox Name="BasicComboBox">
<ComboBoxItem>Item 0</ComboBoxItem>
<ComboBoxItem>Item 1</ComboBoxItem>
</ComboBox>
<Button Name="ComboBoxSelectionClear">Clear Selection</Button>
<Button Name="ComboBoxSelectFirst">Select First</Button>
</StackPanel>
</TabItem>
<TabItem Header="ListBox">
<DockPanel>
<StackPanel DockPanel.Dock="Bottom">
<Button Name="ListBoxSelectionClear">Clear Selection</Button>
</StackPanel>
<ListBox Name="BasicListBox" Items="{Binding ListBoxItems}" SelectionMode="Multiple"/>
</DockPanel>
</TabItem>
<TabItem Header="Menu">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Name="RootMenuItem" Header="_Root">
<MenuItem Name="Child1MenuItem" Header="_Child 1" InputGesture="Ctrl+O" Click="MenuClicked"/>
<MenuItem Name="Child2MenuItem" Header="_Child 1">
<MenuItem Name="GrandchildMenuItem" Header="_Grandchild" Click="MenuClicked"/>
</MenuItem>
</MenuItem>
</Menu>
<TextBlock Name="ClickedMenuItem">None</TextBlock>
</DockPanel>
</TabItem>
</TabControl>
</DockPanel>
</Window>

67
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace IntegrationTestApp
{
public class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
InitializeViewMenu();
this.AttachDevTools();
AddHandler(Button.ClickEvent, OnButtonClick);
ListBoxItems = Enumerable.Range(0, 100).Select(x => "Item " + x).ToList();
DataContext = this;
}
public List<string> ListBoxItems { get; }
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void InitializeViewMenu()
{
var mainTabs = this.FindControl<TabControl>("MainTabs");
var viewMenu = (NativeMenuItem)NativeMenu.GetMenu(this).Items[1];
foreach (TabItem tabItem in mainTabs.Items)
{
var menuItem = new NativeMenuItem
{
Header = (string)tabItem.Header!,
IsChecked = tabItem.IsSelected,
ToggleType = NativeMenuItemToggleType.Radio,
};
menuItem.Click += (s, e) => tabItem.IsSelected = true;
viewMenu.Menu.Items.Add(menuItem);
}
}
private void MenuClicked(object? sender, RoutedEventArgs e)
{
var clickedMenuItemTextBlock = this.FindControl<TextBlock>("ClickedMenuItem");
clickedMenuItemTextBlock.Text = ((MenuItem)sender!).Header.ToString();
}
private void OnButtonClick(object? sender, RoutedEventArgs e)
{
var source = e.Source as Button;
if (source?.Name == "ComboBoxSelectionClear")
this.FindControl<ComboBox>("BasicComboBox").SelectedIndex = -1;
if (source?.Name == "ComboBoxSelectFirst")
this.FindControl<ComboBox>("BasicComboBox").SelectedIndex = 0;
if (source?.Name == "ListBoxSelectionClear")
this.FindControl<ListBox>("BasicListBox").SelectedIndex = -1;
}
}
}

22
samples/IntegrationTestApp/Program.cs

@ -0,0 +1,22 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
namespace IntegrationTestApp
{
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.
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();
}
}

5
samples/IntegrationTestApp/bundle.sh

@ -0,0 +1,5 @@
#!/usr/bin/env bash
cd $(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
dotnet restore -r osx-arm64
dotnet msbuild -t:BundleApp -p:RuntimeIdentifier=osx-arm64 -p:_AvaloniaUseExternalMSBuild=false

11
samples/IntegrationTestApp/nuget.config

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
To use the Avalonia CI feed to get unstable packages, move this file to the root of your solution.
-->
<configuration>
<packageSources>
<add key="AvaloniaCI" value="https://www.myget.org/F/avalonia-ci/api/v2" />
</packageSources>
</configuration>

3
samples/RenderDemo/MainWindow.xaml

@ -66,5 +66,8 @@
<TabItem Header="Path Measurement">
<pages:PathMeasurementPage />
</TabItem>
<TabItem Header="Brushes">
<pages:BrushesPage />
</TabItem>
</controls:HamburgerMenu>
</Window>

71
samples/RenderDemo/Pages/BrushesPage.axaml

@ -0,0 +1,71 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="RenderDemo.Pages.BrushesPage">
<Canvas Background="White" Width="480" Height="360">
<Rectangle Canvas.Left="20" Canvas.Top="20" Width="440" Height="50">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="410,0">
<LinearGradientBrush.Transform>
<TransformGroup>
<ScaleTransform ScaleX="0.5" />
<SkewTransform />
<RotateTransform />
<TranslateTransform X="5" Y="15" />
</TransformGroup>
</LinearGradientBrush.Transform>
<LinearGradientBrush.GradientStops>
<GradientStop Color="Blue" Offset="0" />
<GradientStop Color="Green" Offset="0.5" />
<GradientStop Color="Lime" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<TextBlock Canvas.Left="20" Canvas.Top="70" FontSize="30" Text="scale(0.5) on gradient" />
<Rectangle Canvas.Left="20" Canvas.Top="110" Width="440" Height="50">
<Rectangle.Fill>
<RadialGradientBrush Center="0.0,0.0" GradientOrigin="0.0,0.0" Radius="0.13636364">
<RadialGradientBrush.Transform>
<TransformGroup>
<ScaleTransform />
<SkewTransform AngleX="45" />
<RotateTransform />
<TranslateTransform X="240" Y="45" />
</TransformGroup>
</RadialGradientBrush.Transform>
<RadialGradientBrush.GradientStops>
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="#FFFFA500" Offset="1" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
<TextBlock Canvas.Left="20" Canvas.Top="160" FontSize="30" Text="skewX(45) on gradient" />
<Rectangle Canvas.Left="20" Canvas.Top="210" Width="440" Height="50">
<Rectangle.Fill>
<VisualBrush TileMode="Tile" SourceRect="0,0,20,20" DestinationRect="0,0,20,20" Stretch="None">
<VisualBrush.Transform>
<TransformGroup>
<ScaleTransform ScaleX="2" ScaleY="2" />
<SkewTransform AngleX="45" />
<RotateTransform />
<TranslateTransform X="5" Y="5" />
</TransformGroup>
</VisualBrush.Transform>
<VisualBrush.Visual>
<Canvas Width="20" Height="20">
<Rectangle Canvas.Left="0" Canvas.Top="0" Width="10" Height="10" Fill="Maroon" />
<Rectangle Canvas.Left="10" Canvas.Top="0" Width="10" Height="10" Fill="Green" />
<Rectangle Canvas.Left="0" Canvas.Top="10" Width="10" Height="10" Fill="Blue" />
<Rectangle Canvas.Left="10" Canvas.Top="10" Width="10" Height="10" Fill="Yellow" />
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
<TextBlock Canvas.Left="20" Canvas.Top="260" FontSize="30" Text="scale(2), skewX(45) on pattern" />
</Canvas>
</UserControl>

18
samples/RenderDemo/Pages/BrushesPage.axaml.cs

@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace RenderDemo.Pages;
public class BrushesPage : UserControl
{
public BrushesPage()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

1
samples/interop/WindowsInteropTest/WindowsInteropTest.csproj

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj" />
<ProjectReference Include="..\..\ControlCatalog\ControlCatalog.csproj">
<Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project>

27
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -13,7 +13,6 @@ namespace Avalonia.Android
{
private readonly TView _host;
private readonly InputMethodManager _imm;
private IInputElement _inputElement;
public AndroidInputMethod(TView host)
{
@ -25,7 +24,7 @@ namespace Avalonia.Android
_host.Focusable = true;
_host.FocusableInTouchMode = true;
_host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListner(_host));
_host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListener(_host));
}
public void Reset()
@ -33,8 +32,10 @@ namespace Avalonia.Android
_imm.RestartInput(_host);
}
public void SetActive(bool active)
public void SetClient(ITextInputMethodClient client)
{
var active = client is { };
if (active)
{
_host.RequestFocus();
@ -49,20 +50,8 @@ namespace Avalonia.Android
{
}
public void SetOptions(TextInputOptionsQueryEventArgs options)
public void SetOptions(TextInputOptions options)
{
if (_inputElement != null)
{
_inputElement.PointerReleased -= RestoreSoftKeyboard;
}
_inputElement = options.Source as InputElement;
if (_inputElement == null)
{
_imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.None);
}
_host.InitEditorInfo((outAttrs) =>
{
outAttrs.InputType = options.ContentType switch
@ -70,7 +59,7 @@ namespace Avalonia.Android
TextInputContentType.Email => global::Android.Text.InputTypes.TextVariationEmailAddress,
TextInputContentType.Number => global::Android.Text.InputTypes.ClassNumber,
TextInputContentType.Password => global::Android.Text.InputTypes.TextVariationPassword,
TextInputContentType.Phone => global::Android.Text.InputTypes.ClassPhone,
TextInputContentType.Digits => global::Android.Text.InputTypes.ClassPhone,
TextInputContentType.Url => global::Android.Text.InputTypes.TextVariationUri,
_ => global::Android.Text.InputTypes.ClassText
};
@ -83,9 +72,9 @@ namespace Avalonia.Android
if (options.Multiline)
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine;
});
//_inputElement.PointerReleased += RestoreSoftKeyboard;
outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi;
});
}
private void RestoreSoftKeyboard(object sender, PointerReleasedEventArgs e)

5
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -1,9 +1,9 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Android;
using Avalonia.Android.Platform;
using Avalonia.Android.Platform.Input;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
@ -19,6 +19,7 @@ namespace Avalonia
public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
{
var options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions();
return builder
.UseWindowingSubsystem(() => AndroidPlatform.Initialize(options), "Android")
.UseSkia();

9
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -1,12 +1,19 @@
<Project Sdk="Xamarin.Legacy.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0-android;monoandroid11.0</TargetFrameworks>
<TargetFrameworks>net6.0-android</TargetFrameworks>
<TargetFrameworks Condition="'$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);monoandroid11.0</TargetFrameworks>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<DebugType>portable</DebugType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup>
</Project>

65
src/Android/Avalonia.Android/AvaloniaActivity.cs

@ -1,35 +1,82 @@
using Android.App;
using Android.OS;
using Android.Views;
using AndroidX.AppCompat.App;
using Android.Content.Res;
using AndroidX.Lifecycle;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls;
namespace Avalonia.Android
{
public abstract class AvaloniaActivity : Activity
public abstract class AvaloniaActivity<TApp> : AppCompatActivity where TApp : Application, new()
{
internal class SingleViewLifetime : ISingleViewApplicationLifetime
{
public AvaloniaView View { get; internal set; }
public Control MainView
{
get => (Control)View.Content;
set => View.Content = value;
}
}
internal AvaloniaView View;
object _content;
internal AvaloniaViewModel _viewModel;
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
protected override void OnCreate(Bundle savedInstanceState)
{
var builder = AppBuilder.Configure<TApp>();
CustomizeAppBuilder(builder);
View = new AvaloniaView(this);
if (_content != null)
View.Content = _content;
SetContentView(View);
var lifetime = new SingleViewLifetime();
lifetime.View = View;
builder.AfterSetup(x =>
{
_viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel;
if (_viewModel.Content != null)
{
View.Content = _viewModel.Content;
}
View.Prepare();
});
builder.SetupWithLifetime(lifetime);
base.OnCreate(savedInstanceState);
}
public object Content
{
get
{
return _content;
return _viewModel.Content;
}
set
{
_content = value;
_viewModel.Content = value;
if (View != null)
View.Content = value;
}
}
public override void OnConfigurationChanged(Configuration newConfig)
{
base.OnConfigurationChanged(newConfig);
}
protected override void OnDestroy()
{
View.Content = null;
base.OnDestroy();
}
}
}

7
src/Android/Avalonia.Android/AvaloniaView.cs

@ -12,7 +12,7 @@ namespace Avalonia.Android
{
public class AvaloniaView : FrameLayout
{
private readonly EmbeddableControlRoot _root;
private EmbeddableControlRoot _root;
private readonly ViewImpl _view;
private IDisposable? _timerSubscription;
@ -21,6 +21,11 @@ namespace Avalonia.Android
{
_view = new ViewImpl(context);
AddView(_view.View);
}
internal void Prepare ()
{
_root = new EmbeddableControlRoot(_view);
_root.Prepare();
}

11
src/Android/Avalonia.Android/AvaloniaViewModel.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Android
{
internal class AvaloniaViewModel : AndroidX.Lifecycle.ViewModel
{
public object Content { get; set; }
}
}

50
src/Android/Avalonia.Android/Resources/AboutResources.txt

@ -1,50 +0,0 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable-hdpi/
icon.png
drawable-ldpi/
icon.png
drawable-mdpi/
icon.png
layout/
main.xml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called
"Resource" that contains the tokens for each one of the resources included. For example,
for the above Resources layout, this is what the Resource class would expose:
public class Resource {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first
string in the dictionary file values/strings.xml.

6
src/Android/Avalonia.Android/Resources/Values/Strings.xml

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="Hello">Hello World, Click Me!</string>
<string name="ApplicationName">$projectname$</string>
</resources>

4
src/Android/Avalonia.Android/SoftKeyboardListner.cs → src/Android/Avalonia.Android/SoftKeyboardListener.cs

@ -9,7 +9,7 @@ using Avalonia.Input;
namespace Avalonia.Android
{
class SoftKeyboardListner : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
class SoftKeyboardListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
private const int DefaultKeyboardHeightDP = 100;
private static readonly int EstimatedKeyboardDP = DefaultKeyboardHeightDP + (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop ? 48 : 0);
@ -17,7 +17,7 @@ namespace Avalonia.Android
private readonly View _host;
private bool _wasKeyboard;
public SoftKeyboardListner(View view)
public SoftKeyboardListener(View view)
{
_host = view;
}

8
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@ -9,6 +9,7 @@
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<DebugType>portable</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
@ -21,6 +22,13 @@
<ItemGroup>
<None Remove="Assets\AboutAssets.txt" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<BundleAssemblies>True</BundleAssemblies>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<BundleAssemblies>True</BundleAssemblies>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />

2
src/Android/Avalonia.AndroidTestApplication/MainActivity.cs

@ -14,6 +14,8 @@ namespace Avalonia.AndroidTestApplication
[Activity(Label = "Main",
MainLauncher = true,
Icon = "@drawable/icon",
Theme = "@style/Theme.AppCompat.NoActionBar",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize,
LaunchMode = LaunchMode.SingleInstance/*,
ScreenOrientation = ScreenOrientation.Landscape*/)]
public class MainBaseActivity : AvaloniaActivity

33
src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs

@ -22,19 +22,9 @@ namespace Avalonia.Data.Core.Plugins
var method = GetFirstMethodWithName(instance.GetType(), methodName);
if (method != null)
if (method is not null)
{
var parameters = method.GetParameters();
if (parameters.Length + (method.ReturnType == typeof(void) ? 0 : 1) > 8)
{
var exception = new ArgumentException(
"Cannot create a binding accessor for a method with more than 8 parameters or more than 7 parameters if it has a non-void return type.",
nameof(methodName));
return new PropertyError(new BindingNotification(exception, BindingErrorType.Error));
}
return new Accessor(reference, method, parameters);
return new Accessor(reference, method);
}
else
{
@ -82,18 +72,20 @@ namespace Avalonia.Data.Core.Plugins
private sealed class Accessor : PropertyAccessorBase
{
public Accessor(WeakReference<object?> reference, MethodInfo method, ParameterInfo[] parameters)
public Accessor(WeakReference<object?> reference, MethodInfo method)
{
_ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = method ?? throw new ArgumentNullException(nameof(method));
var returnType = method.ReturnType;
bool hasReturn = returnType != typeof(void);
var signatureTypeCount = (hasReturn ? 1 : 0) + parameters.Length;
var parameters = method.GetParameters();
var signatureTypeCount = parameters.Length + 1;
var paramTypes = new Type[signatureTypeCount];
for (var i = 0; i < parameters.Length; i++)
{
ParameterInfo parameter = parameters[i];
@ -101,16 +93,9 @@ namespace Avalonia.Data.Core.Plugins
paramTypes[i] = parameter.ParameterType;
}
if (hasReturn)
{
paramTypes[paramTypes.Length - 1] = returnType;
paramTypes[paramTypes.Length - 1] = returnType;
PropertyType = Expression.GetFuncType(paramTypes);
}
else
{
PropertyType = Expression.GetActionType(paramTypes);
}
PropertyType = Expression.GetDelegateType(paramTypes);
if (method.IsStatic)
{

10
src/Avalonia.Base/Metadata/TrimSurroundingWhitespaceAttribute.cs

@ -0,0 +1,10 @@
using System;
namespace Avalonia.Metadata
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class TrimSurroundingWhitespaceAttribute : Attribute
{
}
}

12
src/Avalonia.Base/Metadata/WhitespaceSignificantCollectionAttribute.cs

@ -0,0 +1,12 @@
using System;
namespace Avalonia.Metadata
{
/// <summary>
/// Indicates that a collection type should be processed as being whitespace significant by a XAML processor.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class WhitespaceSignificantCollectionAttribute : Attribute
{
}
}

3
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -83,6 +83,9 @@
<Compile Include="../Avalonia.Visuals/Media/Color.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Media/HsvColor.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Visuals/Media/KnownColors.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>

6
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -124,6 +124,9 @@ namespace Avalonia.Build.Tasks
var indexerAccessorClosure = new TypeDefinition("CompiledAvaloniaXaml", "!IndexerAccessorFactoryClosure",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(indexerAccessorClosure);
var trampolineBuilder = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlTrampolines",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(trampolineBuilder);
var (xamlLanguage , emitConfig) = AvaloniaXamlIlLanguage.Configure(typeSystem);
var compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem,
@ -133,6 +136,7 @@ namespace Avalonia.Build.Tasks
AvaloniaXamlIlLanguage.CustomValueConverter,
new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)),
new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)),
new XamlIlTrampolineBuilder(typeSystem.CreateTypeBuilder(trampolineBuilder)),
new DeterministicIdGenerator());
@ -255,6 +259,8 @@ namespace Avalonia.Build.Tasks
true),
(closureName, closureBaseType) =>
populateBuilder.DefineSubType(closureBaseType, closureName, false),
(closureName, returnType, parameterTypes) =>
populateBuilder.DefineDelegateSubType(closureName, false, returnType, parameterTypes),
res.Uri, res
);

1
src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt

@ -0,0 +1 @@
Total Issues: 0

7
src/Avalonia.Controls/ApiCompatBaseline.txt

@ -30,6 +30,7 @@ MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownV
MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.OldValue.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<System.Boolean> Avalonia.StyledProperty<System.Boolean> Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.TopLevel' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Viewbox' does not inherit from base type 'Avalonia.Controls.Decorator' in the implementation but it does in the contract.
MembersMustExist : Member 'public Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.AvaloniaProperty<Avalonia.Media.Stretch> Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Window' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.WindowBase' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
@ -48,7 +49,11 @@ MembersMustExist : Member 'public Avalonia.Media.FormattedText Avalonia.Controls
MembersMustExist : Member 'public System.Int32 Avalonia.Controls.Presenters.TextPresenter.GetCaretIndex(Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Presenters.TextPresenter.InvalidateFormattedText()' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Primitives.PopupRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
TypesMustExist : Type 'Avalonia.Platform.ExportWindowingSubsystemAttribute' does not exist in the implementation but it does exist in the contract.
EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromPoint(Avalonia.PixelPoint)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromRect(Avalonia.PixelRect)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromWindow(Avalonia.Platform.IWindowBaseImpl)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Action<Avalonia.Size, Avalonia.Platform.PlatformResizeReason> Avalonia.Platform.ITopLevelImpl.Resized.get()' is present in the implementation but not in the contract.
@ -67,4 +72,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor
MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract.
Total Issues: 68
Total Issues: 73

28
src/Avalonia.Controls/Automation/AutomationElementIdentifiers.cs

@ -0,0 +1,28 @@
using Avalonia.Automation.Peers;
namespace Avalonia.Automation
{
/// <summary>
/// Contains values used as automation property identifiers by UI Automation providers.
/// </summary>
public static class AutomationElementIdentifiers
{
/// <summary>
/// Identifies the bounding rectangle automation property. The bounding rectangle property
/// value is returned by the <see cref="AutomationPeer.GetBoundingRectangle"/> method.
/// </summary>
public static AutomationProperty BoundingRectangleProperty { get; } = new AutomationProperty();
/// <summary>
/// Identifies the class name automation property. The class name property value is returned
/// by the <see cref="AutomationPeer.GetClassName"/> method.
/// </summary>
public static AutomationProperty ClassNameProperty { get; } = new AutomationProperty();
/// <summary>
/// Identifies the name automation property. The class name property value is returned
/// by the <see cref="AutomationPeer.GetName"/> method.
/// </summary>
public static AutomationProperty NameProperty { get; } = new AutomationProperty();
}
}

28
src/Avalonia.Controls/Automation/AutomationLiveSetting.cs

@ -0,0 +1,28 @@
namespace Avalonia.Automation
{
/// <summary>
/// Describes the notification characteristics of a particular live region
/// </summary>
public enum AutomationLiveSetting
{
/// <summary>
/// The element does not send notifications if the content of the live region has changed.
/// </summary>
Off = 0,
/// <summary>
/// The element sends non-interruptive notifications if the content of the live region has
/// changed. With this setting, UI Automation clients and assistive technologies are expected
/// to not interrupt the user to inform of changes to the live region.
/// </summary>
Polite = 1,
/// <summary>
/// The element sends interruptive notifications if the content of the live region has changed.
/// With this setting, UI Automation clients and assistive technologies are expected to interrupt
/// the user to inform of changes to the live region.
/// </summary>
Assertive = 2,
}
}

630
src/Avalonia.Controls/Automation/AutomationProperties.cs

@ -0,0 +1,630 @@
using System;
using Avalonia.Automation.Peers;
using Avalonia.Controls;
namespace Avalonia.Automation
{
/// <summary>
/// Declares how a control should included in different views of the automation tree.
/// </summary>
public enum AccessibilityView
{
/// <summary>
/// The control is included in the Raw view of the automation tree.
/// </summary>
Raw,
/// <summary>
/// The control is included in the Control view of the automation tree.
/// </summary>
Control,
/// <summary>
/// The control is included in the Content view of the automation tree.
/// </summary>
Content,
}
public static class AutomationProperties
{
internal const int AutomationPositionInSetDefault = -1;
internal const int AutomationSizeOfSetDefault = -1;
/// <summary>
/// Defines the AutomationProperties.AcceleratorKey attached property.
/// </summary>
public static readonly AttachedProperty<string> AcceleratorKeyProperty =
AvaloniaProperty.RegisterAttached<StyledElement, string>(
"AcceleratorKey",
typeof(AutomationProperties));
/// <summary>
/// Defines the AutomationProperties.AccessibilityView attached property.
/// </summary>
public static readonly AttachedProperty<AccessibilityView> AccessibilityViewProperty =
AvaloniaProperty.RegisterAttached<StyledElement, AccessibilityView>(
"AccessibilityView",
typeof(AutomationProperties),
defaultValue: AccessibilityView.Content);
/// <summary>
/// Defines the AutomationProperties.AccessKey attached property
/// </summary>
public static readonly AttachedProperty<string> AccessKeyProperty =
AvaloniaProperty.RegisterAttached<StyledElement, string>(
"AccessKey",
typeof(AutomationProperties));
/// <summary>
/// Defines the AutomationProperties.AutomationId attached property.
/// </summary>
public static readonly AttachedProperty<string> AutomationIdProperty =
AvaloniaProperty.RegisterAttached<StyledElement, string>(
"AutomationId",
typeof(AutomationProperties));
/// <summary>
/// Defines the AutomationProperties.ControlTypeOverride attached property.
/// </summary>
public static readonly AttachedProperty<AutomationControlType?> ControlTypeOverrideProperty =
AvaloniaProperty.RegisterAttached<StyledElement, AutomationControlType?>(
"ControlTypeOverride",
typeof(AutomationProperties));
/// <summary>
/// Defines the AutomationProperties.HelpText attached property.
/// </summary>
public static readonly AttachedProperty<string> HelpTextProperty =
AvaloniaProperty.RegisterAttached<StyledElement, string>(
"HelpText",
typeof(AutomationProperties));
/// <summary>
/// Defines the AutomationProperties.IsColumnHeader attached property.
/// </summary>
public static readonly AttachedProperty<bool> IsColumnHeaderProperty =
AvaloniaProperty.RegisterAttached<StyledElement, bool>(
"IsColumnHeader",
typeof(AutomationProperties),
false);
/// <summary>
/// Defines the AutomationProperties.IsRequiredForForm attached property.
/// </summary>
public static readonly AttachedProperty<bool> IsRequiredForFormProperty =
AvaloniaProperty.RegisterAttached<StyledElement, bool>(
"IsRequiredForForm",
typeof(AutomationProperties),
false);
/// <summary>
/// Defines the AutomationProperties.IsRowHeader attached property.
/// </summary>
public static readonly AttachedProperty<bool> IsRowHeaderProperty =
AvaloniaProperty.RegisterAttached<StyledElement, bool>(
"IsRowHeader",
typeof(AutomationProperties),
false);
/// <summary>
/// Defines the AutomationProperties.IsOffscreenBehavior attached property.
/// </summary>
public static readonly AttachedProperty<IsOffscreenBehavior> IsOffscreenBehaviorProperty =
AvaloniaProperty.RegisterAttached<StyledElement, IsOffscreenBehavior>(
"IsOffscreenBehavior",
typeof(AutomationProperties),
IsOffscreenBehavior.Default);
/// <summary>
/// Defines the AutomationProperties.ItemStatus attached property.
/// </summary>
public static readonly AttachedProperty<string> ItemStatusProperty =
AvaloniaProperty.RegisterAttached<StyledElement, string>(
"ItemStatus",
typeof(AutomationProperties));
/// <summary>
/// Defines the AutomationProperties.ItemType attached property.
/// </summary>
public static readonly AttachedProperty<string> ItemTypeProperty =
AvaloniaProperty.RegisterAttached<StyledElement, string>(
"ItemType",
typeof(AutomationProperties));
/// <summary>
/// Defines the AutomationProperties.LabeledBy attached property.
/// </summary>
public static readonly AttachedProperty<IControl> LabeledByProperty =
AvaloniaProperty.RegisterAttached<StyledElement, IControl>(
"LabeledBy",
typeof(AutomationProperties));
/// <summary>
/// Defines the AutomationProperties.LiveSetting attached property.
/// </summary>
public static readonly AttachedProperty<AutomationLiveSetting> LiveSettingProperty =
AvaloniaProperty.RegisterAttached<StyledElement, AutomationLiveSetting>(
"LiveSetting",
typeof(AutomationProperties),
AutomationLiveSetting.Off);
/// <summary>
/// Defines the AutomationProperties.Name attached attached property.
/// </summary>
public static readonly AttachedProperty<string> NameProperty =
AvaloniaProperty.RegisterAttached<StyledElement, string>(
"Name",
typeof(AutomationProperties));
/// <summary>
/// Defines the AutomationProperties.PositionInSet attached property.
/// </summary>
/// <remarks>
/// The PositionInSet property describes the ordinal location of the element within a set
/// of elements which are considered to be siblings. PositionInSet works in coordination
/// with the SizeOfSet property to describe the ordinal location in the set.
/// </remarks>
public static readonly AttachedProperty<int> PositionInSetProperty =
AvaloniaProperty.RegisterAttached<StyledElement, int>(
"PositionInSet",
typeof(AutomationProperties),
AutomationPositionInSetDefault);
/// <summary>
/// Defines the AutomationProperties.SizeOfSet attached property.
/// </summary>
/// <remarks>
/// The SizeOfSet property describes the count of automation elements in a group or set
/// that are considered to be siblings. SizeOfSet works in coordination with the PositionInSet
/// property to describe the count of items in the set.
/// </remarks>
public static readonly AttachedProperty<int> SizeOfSetProperty =
AvaloniaProperty.RegisterAttached<StyledElement, int>(
"SizeOfSet",
typeof(AutomationProperties),
AutomationSizeOfSetDefault);
/// <summary>
/// Helper for setting AcceleratorKey property on a StyledElement.
/// </summary>
public static void SetAcceleratorKey(StyledElement element, string value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(AcceleratorKeyProperty, value);
}
/// <summary>
/// Helper for reading AcceleratorKey property from a StyledElement.
/// </summary>
public static string GetAcceleratorKey(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((string)element.GetValue(AcceleratorKeyProperty));
}
/// <summary>
/// Helper for setting AccessibilityView property on a StyledElement.
/// </summary>
public static void SetAccessibilityView(StyledElement element, AccessibilityView value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(AccessibilityViewProperty, value);
}
/// <summary>
/// Helper for reading AccessibilityView property from a StyledElement.
/// </summary>
public static AccessibilityView GetAccessibilityView(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return element.GetValue(AccessibilityViewProperty);
}
/// <summary>
/// Helper for setting AccessKey property on a StyledElement.
/// </summary>
public static void SetAccessKey(StyledElement element, string value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(AccessKeyProperty, value);
}
/// <summary>
/// Helper for reading AccessKey property from a StyledElement.
/// </summary>
public static string GetAccessKey(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((string)element.GetValue(AccessKeyProperty));
}
/// <summary>
/// Helper for setting AutomationId property on a StyledElement.
/// </summary>
public static void SetAutomationId(StyledElement element, string value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(AutomationIdProperty, value);
}
/// <summary>
/// Helper for reading AutomationId property from a StyledElement.
/// </summary>
public static string GetAutomationId(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return element.GetValue(AutomationIdProperty);
}
/// <summary>
/// Helper for setting ControlTypeOverride property on a StyledElement.
/// </summary>
public static void SetControlTypeOverride(StyledElement element, AutomationControlType? value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(ControlTypeOverrideProperty, value);
}
/// <summary>
/// Helper for reading ControlTypeOverride property from a StyledElement.
/// </summary>
public static AutomationControlType? GetControlTypeOverride(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return element.GetValue(ControlTypeOverrideProperty);
}
/// <summary>
/// Helper for setting HelpText property on a StyledElement.
/// </summary>
public static void SetHelpText(StyledElement element, string value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(HelpTextProperty, value);
}
/// <summary>
/// Helper for reading HelpText property from a StyledElement.
/// </summary>
public static string GetHelpText(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((string)element.GetValue(HelpTextProperty));
}
/// <summary>
/// Helper for setting IsColumnHeader property on a StyledElement.
/// </summary>
public static void SetIsColumnHeader(StyledElement element, bool value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(IsColumnHeaderProperty, value);
}
/// <summary>
/// Helper for reading IsColumnHeader property from a StyledElement.
/// </summary>
public static bool GetIsColumnHeader(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((bool)element.GetValue(IsColumnHeaderProperty));
}
/// <summary>
/// Helper for setting IsRequiredForForm property on a StyledElement.
/// </summary>
public static void SetIsRequiredForForm(StyledElement element, bool value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(IsRequiredForFormProperty, value);
}
/// <summary>
/// Helper for reading IsRequiredForForm property from a StyledElement.
/// </summary>
public static bool GetIsRequiredForForm(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((bool)element.GetValue(IsRequiredForFormProperty));
}
/// <summary>
/// Helper for reading IsRowHeader property from a StyledElement.
/// </summary>
public static bool GetIsRowHeader(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((bool)element.GetValue(IsRowHeaderProperty));
}
/// <summary>
/// Helper for setting IsRowHeader property on a StyledElement.
/// </summary>
public static void SetIsRowHeader(StyledElement element, bool value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(IsRowHeaderProperty, value);
}
/// <summary>
/// Helper for setting IsOffscreenBehavior property on a StyledElement.
/// </summary>
public static void SetIsOffscreenBehavior(StyledElement element, IsOffscreenBehavior value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(IsOffscreenBehaviorProperty, value);
}
/// <summary>
/// Helper for reading IsOffscreenBehavior property from a StyledElement.
/// </summary>
public static IsOffscreenBehavior GetIsOffscreenBehavior(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((IsOffscreenBehavior)element.GetValue(IsOffscreenBehaviorProperty));
}
/// <summary>
/// Helper for setting ItemStatus property on a StyledElement.
/// </summary>
public static void SetItemStatus(StyledElement element, string value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(ItemStatusProperty, value);
}
/// <summary>
/// Helper for reading ItemStatus property from a StyledElement.
/// </summary>
public static string GetItemStatus(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((string)element.GetValue(ItemStatusProperty));
}
/// <summary>
/// Helper for setting ItemType property on a StyledElement.
/// </summary>
public static void SetItemType(StyledElement element, string value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(ItemTypeProperty, value);
}
/// <summary>
/// Helper for reading ItemType property from a StyledElement.
/// </summary>
public static string GetItemType(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((string)element.GetValue(ItemTypeProperty));
}
/// <summary>
/// Helper for setting LabeledBy property on a StyledElement.
/// </summary>
public static void SetLabeledBy(StyledElement element, IControl value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(LabeledByProperty, value);
}
/// <summary>
/// Helper for reading LabeledBy property from a StyledElement.
/// </summary>
public static IControl GetLabeledBy(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return element.GetValue(LabeledByProperty);
}
/// <summary>
/// Helper for setting LiveSetting property on a StyledElement.
/// </summary>
public static void SetLiveSetting(StyledElement element, AutomationLiveSetting value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(LiveSettingProperty, value);
}
/// <summary>
/// Helper for reading LiveSetting property from a StyledElement.
/// </summary>
public static AutomationLiveSetting GetLiveSetting(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((AutomationLiveSetting)element.GetValue(LiveSettingProperty));
}
/// <summary>
/// Helper for setting Name property on a StyledElement.
/// </summary>
public static void SetName(StyledElement element, string value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(NameProperty, value);
}
/// <summary>
/// Helper for reading Name property from a StyledElement.
/// </summary>
public static string GetName(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((string)element.GetValue(NameProperty));
}
/// <summary>
/// Helper for setting PositionInSet property on a StyledElement.
/// </summary>
public static void SetPositionInSet(StyledElement element, int value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(PositionInSetProperty, value);
}
/// <summary>
/// Helper for reading PositionInSet property from a StyledElement.
/// </summary>
public static int GetPositionInSet(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((int)element.GetValue(PositionInSetProperty));
}
/// <summary>
/// Helper for setting SizeOfSet property on a StyledElement.
/// </summary>
public static void SetSizeOfSet(StyledElement element, int value)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
element.SetValue(SizeOfSetProperty, value);
}
/// <summary>
/// Helper for reading SizeOfSet property from a StyledElement.
/// </summary>
public static int GetSizeOfSet(StyledElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
return ((int)element.GetValue(SizeOfSetProperty));
}
}
}

11
src/Avalonia.Controls/Automation/AutomationProperty.cs

@ -0,0 +1,11 @@
namespace Avalonia.Automation
{
/// <summary>
/// Identifies a property of <see cref="AutomationElementIdentifiers"/> or of a specific
/// control pattern.
/// </summary>
public sealed class AutomationProperty
{
internal AutomationProperty() { }
}
}

21
src/Avalonia.Controls/Automation/AutomationPropertyChangedEventArgs.cs

@ -0,0 +1,21 @@
using System;
namespace Avalonia.Automation
{
public class AutomationPropertyChangedEventArgs : EventArgs
{
public AutomationPropertyChangedEventArgs(
AutomationProperty property,
object? oldValue,
object? newValue)
{
Property = property;
OldValue = oldValue;
NewValue = newValue;
}
public AutomationProperty Property { get; }
public object? OldValue { get; }
public object? NewValue { get; }
}
}

10
src/Avalonia.Controls/Automation/ElementNotEnabledException.cs

@ -0,0 +1,10 @@
using System;
namespace Avalonia.Automation
{
public class ElementNotEnabledException : Exception
{
public ElementNotEnabledException() : base("Element not enabled.") { }
public ElementNotEnabledException(string message) : base(message) { }
}
}

15
src/Avalonia.Controls/Automation/ExpandCollapsePatternIdentifiers.cs

@ -0,0 +1,15 @@
using Avalonia.Automation.Provider;
namespace Avalonia.Automation
{
/// <summary>
/// Contains values used as identifiers by <see cref="IExpandCollapseProvider"/>.
/// </summary>
public static class ExpandCollapsePatternIdentifiers
{
/// <summary>
/// Identifies <see cref="IExpandCollapseProvider.ExpandCollapseState"/> automation property.
/// </summary>
public static AutomationProperty ExpandCollapseStateProperty { get; } = new AutomationProperty();
}
}

29
src/Avalonia.Controls/Automation/ExpandCollapseState.cs

@ -0,0 +1,29 @@
namespace Avalonia.Automation
{
/// <summary>
/// Contains values that specify the <see cref="ExpandCollapseState"/> of a UI Automation element.
/// </summary>
public enum ExpandCollapseState
{
/// <summary>
/// No child nodes, controls, or content of the UI Automation element are displayed.
/// </summary>
Collapsed,
/// <summary>
/// All child nodes, controls or content of the UI Automation element are displayed.
/// </summary>
Expanded,
/// <summary>
/// The UI Automation element has no child nodes, controls, or content to display.
/// </summary>
LeafNode,
/// <summary>
/// Some, but not all, child nodes, controls, or content of the UI Automation element are
/// displayed.
/// </summary>
PartiallyExpanded
}
}

26
src/Avalonia.Controls/Automation/IsOffscreenBehavior.cs

@ -0,0 +1,26 @@
namespace Avalonia.Automation
{
/// <summary>
/// This enum offers different ways of evaluating the IsOffscreen AutomationProperty
/// </summary>
public enum IsOffscreenBehavior
{
/// <summary>
/// The AutomationProperty IsOffscreen is calculated based on IsVisible.
/// </summary>
Default,
/// <summary>
/// The AutomationProperty IsOffscreen is false.
/// </summary>
Onscreen,
/// <summary>
/// The AutomationProperty IsOffscreen if true.
/// </summary>
Offscreen,
/// <summary>
/// The AutomationProperty IsOffscreen is calculated based on clip regions.
/// </summary>
FromClip,
}
}

263
src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs

@ -0,0 +1,263 @@
using System;
using System.Collections.Generic;
namespace Avalonia.Automation.Peers
{
public enum AutomationControlType
{
None,
Button,
Calendar,
CheckBox,
ComboBox,
ComboBoxItem,
Edit,
Hyperlink,
Image,
ListItem,
List,
Menu,
MenuBar,
MenuItem,
ProgressBar,
RadioButton,
ScrollBar,
Slider,
Spinner,
StatusBar,
Tab,
TabItem,
Text,
ToolBar,
ToolTip,
Tree,
TreeItem,
Custom,
Group,
Thumb,
DataGrid,
DataItem,
Document,
SplitButton,
Window,
Pane,
Header,
HeaderItem,
Table,
TitleBar,
Separator,
}
/// <summary>
/// Provides a base class that exposes an element to UI Automation.
/// </summary>
public abstract class AutomationPeer
{
/// <summary>
/// Attempts to bring the element associated with the automation peer into view.
/// </summary>
public void BringIntoView() => BringIntoViewCore();
/// <summary>
/// Gets the accelerator key combinations for the element that is associated with the UI
/// Automation peer.
/// </summary>
public string? GetAcceleratorKey() => GetAcceleratorKeyCore();
/// <summary>
/// Gets the access key for the element that is associated with the automation peer.
/// </summary>
public string? GetAccessKey() => GetAccessKeyCore();
/// <summary>
/// Gets the control type for the element that is associated with the UI Automation peer.
/// </summary>
public AutomationControlType GetAutomationControlType() => GetControlTypeOverrideCore();
/// <summary>
/// Gets the automation ID of the element that is associated with the UI Automation peer.
/// </summary>
public string? GetAutomationId() => GetAutomationIdCore();
/// <summary>
/// Gets the bounding rectangle of the element that is associated with the automation peer
/// in top-level coordinates.
/// </summary>
public Rect GetBoundingRectangle() => GetBoundingRectangleCore();
/// <summary>
/// Gets the child automation peers.
/// </summary>
public IReadOnlyList<AutomationPeer> GetChildren() => GetOrCreateChildrenCore();
/// <summary>
/// Gets a string that describes the class of the element.
/// </summary>
public string GetClassName() => GetClassNameCore() ?? string.Empty;
/// <summary>
/// Gets the automation peer for the label that is targeted to the element.
/// </summary>
/// <returns></returns>
public AutomationPeer? GetLabeledBy() => GetLabeledByCore();
/// <summary>
/// Gets a human-readable localized string that represents the type of the control that is
/// associated with this automation peer.
/// </summary>
public string GetLocalizedControlType() => GetLocalizedControlTypeCore();
/// <summary>
/// Gets text that describes the element that is associated with this automation peer.
/// </summary>
public string GetName() => GetNameCore() ?? string.Empty;
/// <summary>
/// Gets the <see cref="AutomationPeer"/> that is the parent of this <see cref="AutomationPeer"/>.
/// </summary>
/// <returns></returns>
public AutomationPeer? GetParent() => GetParentCore();
/// <summary>
/// Gets a value that indicates whether the element that is associated with this automation
/// peer currently has keyboard focus.
/// </summary>
public bool HasKeyboardFocus() => HasKeyboardFocusCore();
/// <summary>
/// Gets a value that indicates whether the element that is associated with this automation
/// peer contains data that is presented to the user.
/// </summary>
public bool IsContentElement() => IsControlElement() && IsContentElementCore();
/// <summary>
/// Gets a value that indicates whether the element is understood by the user as
/// interactive or as contributing to the logical structure of the control in the GUI.
/// </summary>
public bool IsControlElement() => IsControlElementCore();
/// <summary>
/// Gets a value indicating whether the control is enabled for user interaction.
/// </summary>
public bool IsEnabled() => IsEnabledCore();
/// <summary>
/// Gets a value that indicates whether the element can accept keyboard focus.
/// </summary>
/// <returns></returns>
public bool IsKeyboardFocusable() => IsKeyboardFocusableCore();
/// <summary>
/// Sets the keyboard focus on the element that is associated with this automation peer.
/// </summary>
public void SetFocus() => SetFocusCore();
/// <summary>
/// Shows the context menu for the element that is associated with this automation peer.
/// </summary>
/// <returns>true if a context menu is present for the element; otherwise false.</returns>
public bool ShowContextMenu() => ShowContextMenuCore();
/// <summary>
/// Tries to get a provider of the specified type from the peer.
/// </summary>
/// <typeparam name="T">The provider type.</typeparam>
/// <returns>The provider, or null if not implemented on this peer.</returns>
public T? GetProvider<T>() => (T?)GetProviderCore(typeof(T));
/// <summary>
/// Occurs when the children of the automation peer have changed.
/// </summary>
public event EventHandler? ChildrenChanged;
/// <summary>
/// Occurs when a property value of the automation peer has changed.
/// </summary>
public event EventHandler<AutomationPropertyChangedEventArgs>? PropertyChanged;
/// <summary>
/// Raises an event to notify the automation client the the children of the peer have changed.
/// </summary>
protected void RaiseChildrenChangedEvent() => ChildrenChanged?.Invoke(this, EventArgs.Empty);
/// <summary>
/// Raises an event to notify the automation client of a changed property value.
/// </summary>
/// <param name="property">The property that changed.</param>
/// <param name="oldValue">The previous value of the property.</param>
/// <param name="newValue">The new value of the property.</param>
public void RaisePropertyChangedEvent(
AutomationProperty property,
object? oldValue,
object? newValue)
{
PropertyChanged?.Invoke(this, new AutomationPropertyChangedEventArgs(property, oldValue, newValue));
}
protected virtual string GetLocalizedControlTypeCore()
{
var controlType = GetAutomationControlType();
return controlType switch
{
AutomationControlType.CheckBox => "check box",
AutomationControlType.ComboBox => "combo box",
AutomationControlType.ListItem => "list item",
AutomationControlType.MenuBar => "menu bar",
AutomationControlType.MenuItem => "menu item",
AutomationControlType.ProgressBar => "progress bar",
AutomationControlType.RadioButton => "radio button",
AutomationControlType.ScrollBar => "scroll bar",
AutomationControlType.StatusBar => "status bar",
AutomationControlType.TabItem => "tab item",
AutomationControlType.ToolBar => "toolbar",
AutomationControlType.ToolTip => "tooltip",
AutomationControlType.TreeItem => "tree item",
AutomationControlType.Custom => "custom",
AutomationControlType.DataGrid => "data grid",
AutomationControlType.DataItem => "data item",
AutomationControlType.SplitButton => "split button",
AutomationControlType.HeaderItem => "header item",
AutomationControlType.TitleBar => "title bar",
_ => controlType.ToString().ToLowerInvariant(),
};
}
protected abstract void BringIntoViewCore();
protected abstract string? GetAcceleratorKeyCore();
protected abstract string? GetAccessKeyCore();
protected abstract AutomationControlType GetAutomationControlTypeCore();
protected abstract string? GetAutomationIdCore();
protected abstract Rect GetBoundingRectangleCore();
protected abstract IReadOnlyList<AutomationPeer> GetOrCreateChildrenCore();
protected abstract string GetClassNameCore();
protected abstract AutomationPeer? GetLabeledByCore();
protected abstract string? GetNameCore();
protected abstract AutomationPeer? GetParentCore();
protected abstract bool HasKeyboardFocusCore();
protected abstract bool IsContentElementCore();
protected abstract bool IsControlElementCore();
protected abstract bool IsEnabledCore();
protected abstract bool IsKeyboardFocusableCore();
protected abstract void SetFocusCore();
protected abstract bool ShowContextMenuCore();
protected virtual AutomationControlType GetControlTypeOverrideCore()
{
return GetAutomationControlTypeCore();
}
protected virtual object? GetProviderCore(Type providerType)
{
return providerType.IsAssignableFrom(this.GetType()) ? this : null;
}
protected internal abstract bool TrySetParent(AutomationPeer? parent);
protected void EnsureEnabled()
{
if (!IsEnabled())
throw new ElementNotEnabledException();
}
}
}

43
src/Avalonia.Controls/Automation/Peers/ButtonAutomationPeer.cs

@ -0,0 +1,43 @@
using Avalonia.Automation.Provider;
using Avalonia.Controls;
namespace Avalonia.Automation.Peers
{
public class ButtonAutomationPeer : ContentControlAutomationPeer,
IInvokeProvider
{
public ButtonAutomationPeer(Button owner)
: base(owner)
{
}
public new Button Owner => (Button)base.Owner;
public void Invoke()
{
EnsureEnabled();
(Owner as Button)?.PerformClick();
}
protected override string? GetAcceleratorKeyCore()
{
var result = base.GetAcceleratorKeyCore();
if (string.IsNullOrWhiteSpace(result))
{
result = Owner.HotKey?.ToString();
}
return result;
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Button;
}
protected override bool IsContentElementCore() => true;
protected override bool IsControlElementCore() => true;
}
}

137
src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
namespace Avalonia.Automation.Peers
{
public class ComboBoxAutomationPeer : SelectingItemsControlAutomationPeer,
IExpandCollapseProvider,
IValueProvider
{
private UnrealizedSelectionPeer[]? _selection;
public ComboBoxAutomationPeer(ComboBox owner)
: base(owner)
{
}
public new ComboBox Owner => (ComboBox)base.Owner;
public ExpandCollapseState ExpandCollapseState => ToState(Owner.IsDropDownOpen);
public bool ShowsMenu => true;
public void Collapse() => Owner.IsDropDownOpen = false;
public void Expand() => Owner.IsDropDownOpen = true;
bool IValueProvider.IsReadOnly => true;
string? IValueProvider.Value
{
get
{
var selection = GetSelection();
return selection.Count == 1 ? selection[0].GetName() : null;
}
}
void IValueProvider.SetValue(string? value) => throw new NotSupportedException();
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.ComboBox;
}
protected override IReadOnlyList<AutomationPeer>? GetSelectionCore()
{
if (ExpandCollapseState == ExpandCollapseState.Expanded)
return base.GetSelectionCore();
// If the combo box is not open then we won't have an ItemsPresenter so the default
// GetSelectionCore implementation won't work. For this case we create a separate
// peer to represent the unrealized item.
if (Owner.SelectedItem is object selection)
{
_selection ??= new[] { new UnrealizedSelectionPeer(this) };
_selection[0].Item = selection;
return _selection;
}
return null;
}
protected override void OwnerPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
base.OwnerPropertyChanged(sender, e);
if (e.Property == ComboBox.IsDropDownOpenProperty)
{
RaisePropertyChangedEvent(
ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty,
ToState((bool)e.OldValue!),
ToState((bool)e.NewValue!));
}
}
private ExpandCollapseState ToState(bool value)
{
return value ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed;
}
private class UnrealizedSelectionPeer : UnrealizedElementAutomationPeer
{
private readonly ComboBoxAutomationPeer _owner;
private object? _item;
public UnrealizedSelectionPeer(ComboBoxAutomationPeer owner)
{
_owner = owner;
}
public object? Item
{
get => _item;
set
{
if (_item != value)
{
var oldValue = GetNameCore();
_item = value;
RaisePropertyChangedEvent(
AutomationElementIdentifiers.NameProperty,
oldValue,
GetNameCore());
}
}
}
protected override string? GetAcceleratorKeyCore() => null;
protected override string? GetAccessKeyCore() => null;
protected override string? GetAutomationIdCore() => null;
protected override string GetClassNameCore() => typeof(ComboBoxItem).Name;
protected override AutomationPeer? GetLabeledByCore() => null;
protected override AutomationPeer? GetParentCore() => _owner;
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.ListItem;
protected override string? GetNameCore()
{
if (_item is Control c)
{
var result = AutomationProperties.GetName(c);
if (result is null && c is ContentControl cc && cc.Presenter?.Child is TextBlock text)
{
result = text.Text;
}
if (result is null)
{
result = c.GetValue(ContentControl.ContentProperty)?.ToString();
}
return result;
}
return _item?.ToString();
}
}
}
}

36
src/Avalonia.Controls/Automation/Peers/ContentControlAutomationPeer.cs

@ -0,0 +1,36 @@
using Avalonia.Controls;
namespace Avalonia.Automation.Peers
{
public class ContentControlAutomationPeer : ControlAutomationPeer
{
protected ContentControlAutomationPeer(ContentControl owner)
: base(owner)
{
}
public new ContentControl Owner => (ContentControl)base.Owner;
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Pane;
protected override string? GetNameCore()
{
var result = base.GetNameCore();
if (result is null && Owner.Presenter?.Child is TextBlock text)
{
result = text.Text;
}
if (result is null)
{
result = Owner.Content?.ToString();
}
return result;
}
protected override bool IsContentElementCore() => false;
protected override bool IsControlElementCore() => false;
}
}

221
src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs

@ -0,0 +1,221 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.VisualTree;
namespace Avalonia.Automation.Peers
{
/// <summary>
/// An automation peer which represents a <see cref="Control"/> element.
/// </summary>
public class ControlAutomationPeer : AutomationPeer
{
private IReadOnlyList<AutomationPeer>? _children;
private bool _childrenValid;
private AutomationPeer? _parent;
private bool _parentValid;
public ControlAutomationPeer(Control owner)
{
Owner = owner ?? throw new ArgumentNullException("owner");
Initialize();
}
public Control Owner { get; }
public AutomationPeer GetOrCreate(Control element)
{
if (element == Owner)
return this;
return CreatePeerForElement(element);
}
public static AutomationPeer CreatePeerForElement(Control element)
{
return element.GetOrCreateAutomationPeer();
}
protected override void BringIntoViewCore() => Owner.BringIntoView();
protected override IReadOnlyList<AutomationPeer> GetOrCreateChildrenCore()
{
var children = _children ?? Array.Empty<AutomationPeer>();
if (_childrenValid)
return children;
var newChildren = GetChildrenCore() ?? Array.Empty<AutomationPeer>();
foreach (var peer in children.Except(newChildren))
peer.TrySetParent(null);
foreach (var peer in newChildren)
peer.TrySetParent(this);
_childrenValid = true;
return _children = newChildren;
}
protected virtual IReadOnlyList<AutomationPeer>? GetChildrenCore()
{
var children = ((IVisual)Owner).VisualChildren;
if (children.Count == 0)
return null;
var result = new List<AutomationPeer>();
foreach (var child in children)
{
if (child is Control c && c.IsVisible)
{
result.Add(GetOrCreate(c));
}
}
return result;
}
protected override AutomationPeer? GetLabeledByCore()
{
var label = AutomationProperties.GetLabeledBy(Owner);
return label is Control c ? GetOrCreate(c) : null;
}
protected override string? GetNameCore()
{
var result = AutomationProperties.GetName(Owner);
if (string.IsNullOrWhiteSpace(result) && GetLabeledBy() is AutomationPeer labeledBy)
{
return labeledBy.GetName();
}
return null;
}
protected override AutomationPeer? GetParentCore()
{
EnsureConnected();
return _parent;
}
/// <summary>
/// Invalidates the peer's children and causes a re-read from <see cref="GetChildrenCore"/>.
/// </summary>
protected void InvalidateChildren()
{
_childrenValid = false;
RaiseChildrenChangedEvent();
}
/// <summary>
/// Invalidates the peer's parent.
/// </summary>
protected void InvalidateParent()
{
_parent = null;
_parentValid = false;
}
protected override bool ShowContextMenuCore()
{
var c = Owner;
while (c is object)
{
if (c.ContextMenu is object)
{
c.ContextMenu.Open(c);
return true;
}
c = c.Parent as Control;
}
return false;
}
protected internal override bool TrySetParent(AutomationPeer? parent)
{
_parent = parent;
return true;
}
protected override string? GetAcceleratorKeyCore() => AutomationProperties.GetAcceleratorKey(Owner);
protected override string? GetAccessKeyCore() => AutomationProperties.GetAccessKey(Owner);
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Custom;
protected override string? GetAutomationIdCore() => AutomationProperties.GetAutomationId(Owner) ?? Owner.Name;
protected override Rect GetBoundingRectangleCore() => GetBounds(Owner.TransformedBounds);
protected override string GetClassNameCore() => Owner.GetType().Name;
protected override bool HasKeyboardFocusCore() => Owner.IsFocused;
protected override bool IsContentElementCore() => AutomationProperties.GetAccessibilityView(Owner) >= AccessibilityView.Content;
protected override bool IsControlElementCore() => AutomationProperties.GetAccessibilityView(Owner) >= AccessibilityView.Control;
protected override bool IsEnabledCore() => Owner.IsEnabled;
protected override bool IsKeyboardFocusableCore() => Owner.Focusable;
protected override void SetFocusCore() => Owner.Focus();
protected override AutomationControlType GetControlTypeOverrideCore()
{
return AutomationProperties.GetControlTypeOverride(Owner) ?? GetAutomationControlTypeCore();
}
private static Rect GetBounds(TransformedBounds? bounds)
{
return bounds?.Bounds.TransformToAABB(bounds!.Value.Transform) ?? default;
}
private void Initialize()
{
Owner.PropertyChanged += OwnerPropertyChanged;
var visualChildren = ((IVisual)Owner).VisualChildren;
visualChildren.CollectionChanged += VisualChildrenChanged;
}
private void VisualChildrenChanged(object? sender, EventArgs e) => InvalidateChildren();
private void OwnerPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == Visual.IsVisibleProperty)
{
var parent = Owner.GetVisualParent();
if (parent is Control c)
(GetOrCreate(c) as ControlAutomationPeer)?.InvalidateChildren();
}
else if (e.Property == Visual.TransformedBoundsProperty)
{
RaisePropertyChangedEvent(
AutomationElementIdentifiers.BoundingRectangleProperty,
GetBounds((TransformedBounds?)e.OldValue),
GetBounds((TransformedBounds?)e.NewValue));
}
else if (e.Property == Visual.VisualParentProperty)
{
InvalidateParent();
}
}
private void EnsureConnected()
{
if (!_parentValid)
{
var parent = Owner.GetVisualParent();
while (parent is object)
{
if (parent is Control c)
{
var parentPeer = GetOrCreate(c);
parentPeer.GetChildren();
}
parent = parent.GetVisualParent();
}
_parentValid = true;
}
}
}
}

54
src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs

@ -0,0 +1,54 @@
using Avalonia.Automation.Provider;
using Avalonia.Controls;
namespace Avalonia.Automation.Peers
{
public class ItemsControlAutomationPeer : ControlAutomationPeer, IScrollProvider
{
private bool _searchedForScrollable;
private IScrollProvider? _scroller;
public ItemsControlAutomationPeer(ItemsControl owner)
: base(owner)
{
}
public new ItemsControl Owner => (ItemsControl)base.Owner;
public bool HorizontallyScrollable => _scroller?.HorizontallyScrollable ?? false;
public double HorizontalScrollPercent => _scroller?.HorizontalScrollPercent ?? -1;
public double HorizontalViewSize => _scroller?.HorizontalViewSize ?? 0;
public bool VerticallyScrollable => _scroller?.VerticallyScrollable ?? false;
public double VerticalScrollPercent => _scroller?.VerticalScrollPercent ?? -1;
public double VerticalViewSize => _scroller?.VerticalViewSize ?? 0;
protected virtual IScrollProvider? Scroller
{
get
{
if (!_searchedForScrollable)
{
if (Owner.GetValue(ListBox.ScrollProperty) is Control scrollable)
_scroller = GetOrCreate(scrollable) as IScrollProvider;
_searchedForScrollable = true;
}
return _scroller;
}
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.List;
}
public void Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount)
{
_scroller?.Scroll(horizontalAmount, verticalAmount);
}
public void SetScrollPercent(double horizontalPercent, double verticalPercent)
{
_scroller?.SetScrollPercent(horizontalPercent, verticalPercent);
}
}
}

82
src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs

@ -0,0 +1,82 @@
using System;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Selection;
namespace Avalonia.Automation.Peers
{
public class ListItemAutomationPeer : ContentControlAutomationPeer,
ISelectionItemProvider
{
public ListItemAutomationPeer(ContentControl owner)
: base(owner)
{
}
public bool IsSelected => Owner.GetValue(ListBoxItem.IsSelectedProperty);
public ISelectionProvider? SelectionContainer
{
get
{
if (Owner.Parent is Control parent)
{
var parentPeer = GetOrCreate(parent);
return parentPeer as ISelectionProvider;
}
return null;
}
}
public void Select()
{
EnsureEnabled();
if (Owner.Parent is SelectingItemsControl parent)
{
var index = parent.ItemContainerGenerator.IndexFromContainer(Owner);
if (index != -1)
parent.SelectedIndex = index;
}
}
void ISelectionItemProvider.AddToSelection()
{
EnsureEnabled();
if (Owner.Parent is ItemsControl parent &&
parent.GetValue(ListBox.SelectionProperty) is ISelectionModel selectionModel)
{
var index = parent.ItemContainerGenerator.IndexFromContainer(Owner);
if (index != -1)
selectionModel.Select(index);
}
}
void ISelectionItemProvider.RemoveFromSelection()
{
EnsureEnabled();
if (Owner.Parent is ItemsControl parent &&
parent.GetValue(ListBox.SelectionProperty) is ISelectionModel selectionModel)
{
var index = parent.ItemContainerGenerator.IndexFromContainer(Owner);
if (index != -1)
selectionModel.Deselect(index);
}
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.ListItem;
}
protected override bool IsContentElementCore() => true;
protected override bool IsControlElementCore() => true;
}
}

59
src/Avalonia.Controls/Automation/Peers/MenuItemAutomationPeer.cs

@ -0,0 +1,59 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
namespace Avalonia.Automation.Peers
{
public class MenuItemAutomationPeer : ControlAutomationPeer
{
public MenuItemAutomationPeer(MenuItem owner)
: base(owner)
{
}
public new MenuItem Owner => (MenuItem)base.Owner;
protected override string? GetAccessKeyCore()
{
var result = base.GetAccessKeyCore();
if (string.IsNullOrWhiteSpace(result))
{
if (Owner.HeaderPresenter?.Child is AccessText accessText)
{
result = accessText.AccessKey.ToString();
}
}
return result;
}
protected override string? GetAcceleratorKeyCore()
{
var result = base.GetAcceleratorKeyCore();
if (string.IsNullOrWhiteSpace(result))
{
result = Owner.InputGesture?.ToString();
}
return result;
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.MenuItem;
}
protected override string? GetNameCore()
{
var result = base.GetNameCore();
if (result is null && Owner.Header is string header)
{
result = AccessText.RemoveAccessKeyMarker(header);
}
return result;
}
}
}

25
src/Avalonia.Controls/Automation/Peers/NoneAutomationPeer.cs

@ -0,0 +1,25 @@
using Avalonia.Controls;
namespace Avalonia.Automation.Peers
{
/// <summary>
/// An automation peer which represents an element that is exposed to automation as non-
/// interactive or as not contributing to the logical structure of the application.
/// </summary>
public class NoneAutomationPeer : ControlAutomationPeer
{
public NoneAutomationPeer(Control owner)
: base(owner)
{
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.None;
}
protected override bool IsContentElementCore() => false;
protected override bool IsControlElementCore() => false;
}
}

48
src/Avalonia.Controls/Automation/Peers/PopupAutomationPeer.cs

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Controls.Diagnostics;
using Avalonia.Controls.Primitives;
namespace Avalonia.Automation.Peers
{
public class PopupAutomationPeer : ControlAutomationPeer
{
public PopupAutomationPeer(Popup owner)
: base(owner)
{
owner.Opened += PopupOpenedClosed;
owner.Closed += PopupOpenedClosed;
}
protected override IReadOnlyList<AutomationPeer>? GetChildrenCore()
{
var host = (IPopupHostProvider)Owner;
return host.PopupHost is Control c ? new[] { GetOrCreate(c) } : null;
}
protected override bool IsContentElementCore() => false;
protected override bool IsControlElementCore() => false;
private void PopupOpenedClosed(object? sender, EventArgs e)
{
// This is golden. We're following WPF's automation peer API here where the
// parent of a peer is set when another peer returns it as a child. We want to
// add the popup root as a child of the popup, so we need to return it as a
// child right? Yeah except invalidating children doesn't automatically cause
// UIA to re-read the children meaning that the parent doesn't get set. So the
// MAIN MECHANISM FOR PARENTING CONTROLS IS BROKEN WITH THE ONLY AUTOMATION API
// IT WAS WRITTEN FOR. Luckily WPF provides an escape-hatch by exposing the
// TrySetParent API internally to work around this. We're exposing it publicly
// to shame whoever came up with this abomination of an API.
GetPopupRoot()?.TrySetParent(this);
InvalidateChildren();
}
private AutomationPeer? GetPopupRoot()
{
var popupRoot = ((IPopupHostProvider)Owner).PopupHost as Control;
return popupRoot is object ? GetOrCreate(popupRoot) : null;
}
}
}

40
src/Avalonia.Controls/Automation/Peers/PopupRootAutomationPeer.cs

@ -0,0 +1,40 @@
using System;
using Avalonia.Controls.Primitives;
namespace Avalonia.Automation.Peers
{
public class PopupRootAutomationPeer : WindowBaseAutomationPeer
{
public PopupRootAutomationPeer(PopupRoot owner)
: base(owner)
{
if (owner.IsVisible)
StartTrackingFocus();
else
owner.Opened += OnOpened;
owner.Closed += OnClosed;
}
protected override bool IsContentElementCore() => false;
protected override bool IsControlElementCore() => false;
protected override AutomationPeer? GetParentCore()
{
var parent = base.GetParentCore();
return parent;
}
private void OnOpened(object? sender, EventArgs e)
{
((PopupRoot)Owner).Opened -= OnOpened;
StartTrackingFocus();
}
private void OnClosed(object? sender, EventArgs e)
{
((PopupRoot)Owner).Closed -= OnClosed;
StopTrackingFocus();
}
}
}

34
src/Avalonia.Controls/Automation/Peers/RangeBaseAutomationPeer.cs

@ -0,0 +1,34 @@
using Avalonia.Automation.Provider;
using Avalonia.Controls.Primitives;
namespace Avalonia.Automation.Peers
{
public abstract class RangeBaseAutomationPeer : ControlAutomationPeer, IRangeValueProvider
{
public RangeBaseAutomationPeer(RangeBase owner)
: base(owner)
{
owner.PropertyChanged += OwnerPropertyChanged;
}
public new RangeBase Owner => (RangeBase)base.Owner;
public virtual bool IsReadOnly => false;
public double Maximum => Owner.Maximum;
public double Minimum => Owner.Minimum;
public double Value => Owner.Value;
public double SmallChange => Owner.SmallChange;
public double LargeChange => Owner.LargeChange;
public void SetValue(double value) => Owner.Value = value;
protected virtual void OwnerPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == RangeBase.MinimumProperty)
RaisePropertyChangedEvent(RangeValuePatternIdentifiers.MinimumProperty, e.OldValue, e.NewValue);
else if (e.Property == RangeBase.MaximumProperty)
RaisePropertyChangedEvent(RangeValuePatternIdentifiers.MaximumProperty, e.OldValue, e.NewValue);
else if (e.Property == RangeBase.ValueProperty)
RaisePropertyChangedEvent(RangeValuePatternIdentifiers.ValueProperty, e.OldValue, e.NewValue);
}
}
}

172
src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs

@ -0,0 +1,172 @@
using System;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
using Avalonia.Utilities;
namespace Avalonia.Automation.Peers
{
public class ScrollViewerAutomationPeer : ControlAutomationPeer, IScrollProvider
{
public ScrollViewerAutomationPeer(ScrollViewer owner)
: base(owner)
{
}
public new ScrollViewer Owner => (ScrollViewer)base.Owner;
public bool HorizontallyScrollable
{
get => MathUtilities.GreaterThan(Owner.Extent.Width, Owner.Viewport.Width);
}
public double HorizontalScrollPercent
{
get
{
if (!HorizontallyScrollable)
return ScrollPatternIdentifiers.NoScroll;
return (double)(Owner.Offset.X * 100.0 / (Owner.Extent.Width - Owner.Viewport.Width));
}
}
public double HorizontalViewSize
{
get
{
if (MathUtilities.IsZero(Owner.Extent.Width))
return 100;
return Math.Min(100, Owner.Viewport.Width * 100.0 / Owner.Extent.Width);
}
}
public bool VerticallyScrollable
{
get => MathUtilities.GreaterThan(Owner.Extent.Height, Owner.Viewport.Height);
}
public double VerticalScrollPercent
{
get
{
if (!VerticallyScrollable)
return ScrollPatternIdentifiers.NoScroll;
return (double)(Owner.Offset.Y * 100.0 / (Owner.Extent.Height - Owner.Viewport.Height));
}
}
public double VerticalViewSize
{
get
{
if (MathUtilities.IsZero(Owner.Extent.Height))
return 100;
return Math.Min(100, Owner.Viewport.Height * 100.0 / Owner.Extent.Height);
}
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Pane;
}
protected override bool IsContentElementCore() => false;
protected override bool IsControlElementCore()
{
// Return false if the control is part of a control template.
return Owner.TemplatedParent is null && base.IsControlElementCore();
}
public void Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount)
{
if (!IsEnabled())
throw new ElementNotEnabledException();
var scrollHorizontally = horizontalAmount != ScrollAmount.NoAmount;
var scrollVertically = verticalAmount != ScrollAmount.NoAmount;
if (scrollHorizontally && !HorizontallyScrollable || scrollVertically && !VerticallyScrollable)
{
throw new InvalidOperationException("Operation cannot be performed");
}
switch (horizontalAmount)
{
case ScrollAmount.LargeDecrement:
Owner.PageLeft();
break;
case ScrollAmount.SmallDecrement:
Owner.LineLeft();
break;
case ScrollAmount.SmallIncrement:
Owner.LineRight();
break;
case ScrollAmount.LargeIncrement:
Owner.PageRight();
break;
case ScrollAmount.NoAmount:
break;
default:
throw new InvalidOperationException("Operation cannot be performed");
}
switch (verticalAmount)
{
case ScrollAmount.LargeDecrement:
Owner.PageUp();
break;
case ScrollAmount.SmallDecrement:
Owner.LineUp();
break;
case ScrollAmount.SmallIncrement:
Owner.LineDown();
break;
case ScrollAmount.LargeIncrement:
Owner.PageDown();
break;
case ScrollAmount.NoAmount:
break;
default:
throw new InvalidOperationException("Operation cannot be performed");
}
}
public void SetScrollPercent(double horizontalPercent, double verticalPercent)
{
if (!IsEnabled())
throw new ElementNotEnabledException();
var scrollHorizontally = horizontalPercent != ScrollPatternIdentifiers.NoScroll;
var scrollVertically = verticalPercent != ScrollPatternIdentifiers.NoScroll;
if (scrollHorizontally && !HorizontallyScrollable || scrollVertically && !VerticallyScrollable)
{
throw new InvalidOperationException("Operation cannot be performed");
}
if (scrollHorizontally && (horizontalPercent < 0.0) || (horizontalPercent > 100.0))
{
throw new ArgumentOutOfRangeException("horizontalPercent");
}
if (scrollVertically && (verticalPercent < 0.0) || (verticalPercent > 100.0))
{
throw new ArgumentOutOfRangeException("verticalPercent");
}
var offset = Owner.Offset;
if (scrollHorizontally)
{
offset = offset.WithX((Owner.Extent.Width - Owner.Viewport.Width) * horizontalPercent * 0.01);
}
if (scrollVertically)
{
offset = offset.WithY((Owner.Extent.Height - Owner.Viewport.Height) * verticalPercent * 0.01);
}
Owner.Offset = offset;
}
}
}

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

Loading…
Cancel
Save