Browse Source

Merge branch 'master' into android-auto-detect-night-mode

# Conflicts:
#	api/Avalonia.nupkg.xml
#	src/Tizen/Avalonia.Tizen/TopLevelImpl.cs
pull/16340/head
Julien Lebosquain 2 months ago
parent
commit
bfdb095412
No known key found for this signature in database GPG Key ID: 1833CAD10ACC46FD
  1. 3
      .editorconfig
  2. 192
      .nuke/build.schema.json
  3. 10
      Avalonia.Desktop.slnf
  4. 139
      Avalonia.sln
  5. 4
      CONTRIBUTING.md
  6. 4
      Directory.Build.props
  7. 16
      NuGet.Config
  8. 22
      api/Avalonia.Android.nupkg.xml
  9. 22
      api/Avalonia.Browser.nupkg.xml
  10. 10
      api/Avalonia.FreeDesktop.nupkg.xml
  11. 16
      api/Avalonia.Skia.nupkg.xml
  12. 16
      api/Avalonia.Themes.Fluent.nupkg.xml
  13. 214
      api/Avalonia.Win32.nupkg.xml
  14. 16
      api/Avalonia.iOS.nupkg.xml
  15. 272
      api/Avalonia.nupkg.xml
  16. 28
      azure-pipelines-integrationtests.yml
  17. 34
      azure-pipelines.yml
  18. 2
      build/AnalyzerProject.targets
  19. 12
      build/Base.props
  20. 2
      build/DevSingleProject.targets
  21. 6
      build/HarfBuzzSharp.props
  22. 2
      build/ImageSharp.props
  23. 5
      build/JetBrains.dotMemoryUnit.props
  24. 2
      build/Microsoft.Reactive.Testing.props
  25. 2
      build/Moq.props
  26. 5
      build/ReactiveUI.props
  27. 2
      build/Rx.props
  28. 1
      build/SharedVersion.props
  29. 6
      build/SkiaSharp.props
  30. 4
      build/SourceLink.props
  31. 19
      build/TargetFrameworks.props
  32. 5
      build/UnitTests.NetCore.targets
  33. 18
      build/XUnit.props
  34. 10
      dirs.proj
  35. 2
      docs/api-compat.md
  36. 9
      docs/build.md
  37. 2
      docs/porting-code-from-3rd-party-sources.md
  38. 2
      external/XamlX
  39. 2
      global.json
  40. 3
      native/Avalonia.Native/inc/com.h
  41. 5
      native/Avalonia.Native/inc/noarc.h
  42. 16
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  43. 1
      native/Avalonia.Native/src/OSX/AvnView.h
  44. 25
      native/Avalonia.Native/src/OSX/AvnView.mm
  45. 155
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  46. 2
      native/Avalonia.Native/src/OSX/KeyTransform.mm
  47. 43
      native/Avalonia.Native/src/OSX/StorageProvider.mm
  48. 8
      native/Avalonia.Native/src/OSX/TopLevelImpl.h
  49. 62
      native/Avalonia.Native/src/OSX/TopLevelImpl.mm
  50. 4
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  51. 45
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  52. 4
      native/Avalonia.Native/src/OSX/WindowImpl.h
  53. 54
      native/Avalonia.Native/src/OSX/automation.mm
  54. 55
      native/Avalonia.Native/src/OSX/cgl.mm
  55. 7
      native/Avalonia.Native/src/OSX/clipboard.h
  56. 420
      native/Avalonia.Native/src/OSX/clipboard.mm
  57. 5
      native/Avalonia.Native/src/OSX/common.h
  58. 9
      native/Avalonia.Native/src/OSX/crapium.h
  59. 21
      native/Avalonia.Native/src/OSX/crapium.mm
  60. 14
      native/Avalonia.Native/src/OSX/dnd.mm
  61. 29
      native/Avalonia.Native/src/OSX/main.mm
  62. 40
      native/Avalonia.Native/src/OSX/memhelp.mm
  63. 4
      native/Avalonia.Native/src/OSX/menu.mm
  64. 169
      native/Avalonia.Native/src/OSX/metal.mm
  65. 16
      native/Avalonia.Native/src/OSX/noarc.mm
  66. 6
      native/Avalonia.Native/src/OSX/trayicon.mm
  67. 639
      nukebuild/ApiDiffHelper.cs
  68. 199
      nukebuild/Build.cs
  69. 52
      nukebuild/BuildParameters.cs
  70. 2
      nukebuild/BuildTasksPatcher.cs
  71. 25
      nukebuild/ByteArrayEqualityComparer.cs
  72. 20
      nukebuild/_build.csproj
  73. 12
      packages/Avalonia/Avalonia.csproj
  74. 5
      packages/Avalonia/Avalonia.props
  75. 2
      packages/Avalonia/AvaloniaPrivateApis.targets
  76. 4
      packages/Avalonia/AvaloniaSingleProject.targets
  77. 2
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  78. 10
      samples/ControlCatalog.Browser.Blazor/App.razor
  79. 10
      samples/ControlCatalog.Browser.Blazor/App.razor.cs
  80. 29
      samples/ControlCatalog.Browser.Blazor/ControlCatalog.Browser.Blazor.csproj
  81. 5
      samples/ControlCatalog.Browser.Blazor/Pages/Index.razor
  82. 39
      samples/ControlCatalog.Browser.Blazor/Program.cs
  83. 22
      samples/ControlCatalog.Browser.Blazor/Properties/launchSettings.json
  84. 7
      samples/ControlCatalog.Browser.Blazor/Shared/MainLayout.razor
  85. 10
      samples/ControlCatalog.Browser.Blazor/_Imports.razor
  86. 56
      samples/ControlCatalog.Browser.Blazor/wwwroot/css/app.css
  87. BIN
      samples/ControlCatalog.Browser.Blazor/wwwroot/favicon.ico
  88. 22
      samples/ControlCatalog.Browser.Blazor/wwwroot/index.html
  89. 35
      samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
  90. 2
      samples/ControlCatalog.Desktop/NativeControls/Gtk/EmbedSample.Gtk.cs
  91. 2
      samples/ControlCatalog.Desktop/NativeControls/Gtk/GtkHelper.cs
  92. 0
      samples/ControlCatalog.Desktop/NativeControls/Gtk/nodes-license.md
  93. 0
      samples/ControlCatalog.Desktop/NativeControls/Gtk/nodes.mp4
  94. 2
      samples/ControlCatalog.Desktop/NativeControls/Mac/EmbedSample.Mac.cs
  95. 2
      samples/ControlCatalog.Desktop/NativeControls/Mac/MacHelper.cs
  96. 2
      samples/ControlCatalog.Desktop/NativeControls/Win/EmbedSample.Win.cs
  97. 2
      samples/ControlCatalog.Desktop/NativeControls/Win/WinApi.cs
  98. 172
      samples/ControlCatalog.Desktop/Program.cs
  99. 2
      samples/ControlCatalog.Desktop/Properties/launchSettings.json
  100. 0
      samples/ControlCatalog.Desktop/app.manifest

3
.editorconfig

@ -141,9 +141,6 @@ dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomme
# CS0649: Field 'field' is never assigned to, and will always have its default value 'value'
dotnet_diagnostic.CS0649.severity = error
# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = suggestion
# CS0162: Remove unreachable code
dotnet_diagnostic.CS0162.severity = error
# CA1018: Mark attributes with AttributeUsageAttribute

192
.nuke/build.schema.json

@ -1,48 +1,78 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Build Schema",
"$ref": "#/definitions/build",
"definitions": {
"build": {
"type": "object",
"Host": {
"type": "string",
"enum": [
"AppVeyor",
"AzurePipelines",
"Bamboo",
"Bitbucket",
"Bitrise",
"GitHubActions",
"GitLab",
"Jenkins",
"Rider",
"SpaceAutomation",
"TeamCity",
"Terminal",
"TravisCI",
"VisualStudio",
"VSCode"
]
},
"ExecutableTarget": {
"type": "string",
"enum": [
"BuildToNuGetCache",
"CiAzureLinux",
"CiAzureOSX",
"CiAzureWindows",
"Clean",
"Compile",
"CompileNative",
"CreateIntermediateNugetPackages",
"CreateNugetPackages",
"DownloadApiBaselinePackages",
"GenerateCppHeaders",
"InitDnx",
"OutputApiDiff",
"OutputVersion",
"Package",
"RunCoreLibsTests",
"RunHtmlPreviewerTests",
"RunLeakTests",
"RunRenderTests",
"RunTests",
"RunToolsTests",
"ValidateApiDiff",
"VerifyXamlCompilation",
"ZipFiles"
]
},
"Verbosity": {
"type": "string",
"description": "",
"enum": [
"Verbose",
"Normal",
"Minimal",
"Quiet"
]
},
"NukeBuild": {
"properties": {
"api-baseline": {
"type": "string"
},
"configuration": {
"type": "string"
},
"Continue": {
"type": "boolean",
"description": "Indicates to continue a previously failed build attempt"
},
"force-nuget-version": {
"type": "string"
},
"Help": {
"type": "boolean",
"description": "Shows the help text for this build assembly"
},
"Host": {
"type": "string",
"description": "Host for execution. Default is 'automatic'",
"enum": [
"AppVeyor",
"AzurePipelines",
"Bamboo",
"Bitbucket",
"Bitrise",
"GitHubActions",
"GitLab",
"Jenkins",
"Rider",
"SpaceAutomation",
"TeamCity",
"Terminal",
"TravisCI",
"VisualStudio",
"VSCode"
]
"$ref": "#/definitions/Host"
},
"NoLogo": {
"type": "boolean",
@ -71,89 +101,51 @@
"type": "array",
"description": "List of targets to be skipped. Empty list skips all dependencies",
"items": {
"type": "string",
"enum": [
"BuildToNuGetCache",
"CiAzureLinux",
"CiAzureOSX",
"CiAzureWindows",
"Clean",
"Compile",
"CompileHtmlPreviewer",
"CompileNative",
"CreateIntermediateNugetPackages",
"CreateNugetPackages",
"GenerateCppHeaders",
"OutputApiDiff",
"OutputVersion",
"Package",
"RunCoreLibsTests",
"RunHtmlPreviewerTests",
"RunLeakTests",
"RunRenderTests",
"RunTests",
"RunToolsTests",
"ValidateApiDiff",
"VerifyXamlCompilation",
"ZipFiles"
]
"$ref": "#/definitions/ExecutableTarget"
}
},
"skip-previewer": {
"type": "boolean"
},
"skip-tests": {
"type": "boolean"
},
"Target": {
"type": "array",
"description": "List of targets to be invoked. Default is '{default_target}'",
"items": {
"type": "string",
"enum": [
"BuildToNuGetCache",
"CiAzureLinux",
"CiAzureOSX",
"CiAzureWindows",
"Clean",
"Compile",
"CompileHtmlPreviewer",
"CompileNative",
"CreateIntermediateNugetPackages",
"CreateNugetPackages",
"GenerateCppHeaders",
"OutputApiDiff",
"OutputVersion",
"Package",
"RunCoreLibsTests",
"RunHtmlPreviewerTests",
"RunLeakTests",
"RunRenderTests",
"RunTests",
"RunToolsTests",
"ValidateApiDiff",
"VerifyXamlCompilation",
"ZipFiles"
]
"$ref": "#/definitions/ExecutableTarget"
}
},
"update-api-suppression": {
"type": "boolean"
},
"Verbosity": {
"type": "string",
"description": "Logging verbosity during build execution. Default is 'Normal'",
"enum": [
"Minimal",
"Normal",
"Quiet",
"Verbose"
"$ref": "#/definitions/Verbosity"
}
}
}
},
"allOf": [
{
"properties": {
"configuration": {
"type": "string"
},
"force-api-baseline": {
"type": "string"
},
"force-nuget-version": {
"type": "string"
},
"skip-tests": {
"type": "boolean"
},
"update-api-suppression": {
"type": [
"boolean",
"null"
]
},
"version-output-dir": {
"type": "string"
}
}
},
{
"$ref": "#/definitions/NukeBuild"
}
}
}
]
}

10
Avalonia.Desktop.slnf

@ -1,16 +1,15 @@
{
{
"solution": {
"path": "Avalonia.sln",
"projects": [
"packages\\Avalonia\\Avalonia.csproj",
"samples\\AppWithoutLifetime\\AppWithoutLifetime.csproj",
"samples\\ControlCatalog.NetCore\\ControlCatalog.NetCore.csproj",
"samples\\ControlCatalog.Desktop\\ControlCatalog.Desktop.csproj",
"samples\\ControlCatalog\\ControlCatalog.csproj",
"samples\\GpuInterop\\GpuInterop.csproj",
"samples\\IntegrationTestApp\\IntegrationTestApp.csproj",
"samples\\TextTestApp\\TextTestApp.csproj",
"samples\\MiniMvvm\\MiniMvvm.csproj",
"samples\\ReactiveUIDemo\\ReactiveUIDemo.csproj",
"samples\\RenderDemo\\RenderDemo.csproj",
"samples\\SampleControls\\ControlSamples.csproj",
"samples\\Sandbox\\Sandbox.csproj",
@ -32,7 +31,6 @@
"src\\Avalonia.Native\\Avalonia.Native.csproj",
"src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj",
"src\\Avalonia.Vulkan\\Avalonia.Vulkan.csproj",
"src\\Avalonia.ReactiveUI\\Avalonia.ReactiveUI.csproj",
"src\\Avalonia.Remote.Protocol\\Avalonia.Remote.Protocol.csproj",
"src\\Avalonia.Themes.Fluent\\Avalonia.Themes.Fluent.csproj",
"src\\Avalonia.Themes.Simple\\Avalonia.Themes.Simple.csproj",
@ -48,7 +46,6 @@
"src\\tools\\Avalonia.Generators\\Avalonia.Generators.csproj",
"src\\tools\\DevAnalyzers\\DevAnalyzers.csproj",
"src\\tools\\DevGenerators\\DevGenerators.csproj",
"src\\Windows\\Avalonia.Direct2D1\\Avalonia.Direct2D1.csproj",
"src\\Windows\\Avalonia.Win32.Automation\\Avalonia.Win32.Automation.csproj",
"src\\Windows\\Avalonia.Win32.Interoperability\\Avalonia.Win32.Interoperability.csproj",
"src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj",
@ -58,14 +55,11 @@
"tests\\Avalonia.Controls.UnitTests\\Avalonia.Controls.UnitTests.csproj",
"tests\\Avalonia.DesignerSupport.TestApp\\Avalonia.DesignerSupport.TestApp.csproj",
"tests\\Avalonia.DesignerSupport.Tests\\Avalonia.DesignerSupport.Tests.csproj",
"tests\\Avalonia.Direct2D1.RenderTests\\Avalonia.Direct2D1.RenderTests.csproj",
"tests\\Avalonia.Direct2D1.UnitTests\\Avalonia.Direct2D1.UnitTests.csproj",
"tests\\Avalonia.Generators.Tests\\Avalonia.Generators.Tests.csproj",
"tests\\Avalonia.IntegrationTests.Appium\\Avalonia.IntegrationTests.Appium.csproj",
"tests\\Avalonia.LeakTests\\Avalonia.LeakTests.csproj",
"tests\\Avalonia.Markup.UnitTests\\Avalonia.Markup.UnitTests.csproj",
"tests\\Avalonia.Markup.Xaml.UnitTests\\Avalonia.Markup.Xaml.UnitTests.csproj",
"tests\\Avalonia.ReactiveUI.UnitTests\\Avalonia.ReactiveUI.UnitTests.csproj",
"tests\\Avalonia.RenderTests.WpfCompare\\Avalonia.RenderTests.WpfCompare.csproj",
"tests\\Avalonia.Skia.RenderTests\\Avalonia.Skia.RenderTests.csproj",
"tests\\Avalonia.Skia.UnitTests\\Avalonia.Skia.UnitTests.csproj",

139
Avalonia.sln

@ -9,8 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Windows", "Windows", "{B39A
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32", "src\Windows\Avalonia.Win32\Avalonia.Win32.csproj", "{811A76CF-1CF6-440F-963B-BBE31BD72A82}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1", "src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj", "{3E908F67-5543-4879-A1DC-08EACE79B3CD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls", "src\Avalonia.Controls\Avalonia.Controls.csproj", "{D2221C82-4A25-4583-9B43-D791E3F6820C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Simple", "src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}"
@ -23,10 +21,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.UnitTests
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base.UnitTests", "tests\Avalonia.Base.UnitTests\Avalonia.Base.UnitTests.csproj", "{2905FF23-53FB-45E6-AA49-6AF47A172056}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.RenderTests", "tests\Avalonia.Direct2D1.RenderTests\Avalonia.Direct2D1.RenderTests.csproj", "{DABFD304-D6A4-4752-8123-C2CCF7AC7831}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.UnitTests", "tests\Avalonia.Direct2D1.UnitTests\Avalonia.Direct2D1.UnitTests.csproj", "{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.UnitTests", "tests\Avalonia.Markup.Xaml.UnitTests\Avalonia.Markup.Xaml.UnitTests.csproj", "{99135EAB-653D-47E4-A378-C96E1278CA44}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Markup", "Markup", "{8B6A8209-894F-4BA1-B880-965FD453982C}"
@ -38,15 +32,14 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
src\Shared\CallerArgumentExpressionAttribute.cs = src\Shared\CallerArgumentExpressionAttribute.cs
src\Shared\IsExternalInit.cs = src\Shared\IsExternalInit.cs
src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs
src\Shared\NullableAttributes.cs = src\Shared\NullableAttributes.cs
src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs
src\Shared\StringCompatibilityExtensions.cs = src\Shared\StringCompatibilityExtensions.cs
src\Shared\StreamCompatibilityExtensions.cs = src\Shared\StreamCompatibilityExtensions.cs
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup", "src\Markup\Avalonia.Markup\Avalonia.Markup.csproj", "{6417E941-21BC-467B-A771-0DE389353CE6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.UnitTests", "tests\Avalonia.Markup.UnitTests\Avalonia.Markup.UnitTests.csproj", "{8EF392D5-1416-45AA-9956-7CBBC3229E8A}"
@ -87,8 +80,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Android", "s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia", "src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{7D2D3083-71DD-4CC9-8907-39A0D86FB322}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "samples\ControlCatalog.NetCore\ControlCatalog.NetCore.csproj", "{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
build\AvaloniaPublicKey.props = build\AvaloniaPublicKey.props
@ -99,7 +90,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\EmbedXaml.props = build\EmbedXaml.props
build\HarfBuzzSharp.props = build\HarfBuzzSharp.props
build\ImageSharp.props = build\ImageSharp.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Microsoft.CSharp.props = build\Microsoft.CSharp.props
build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props
build\Moq.props = build\Moq.props
@ -107,12 +97,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\NetCore.props = build\NetCore.props
build\NetFX.props = build\NetFX.props
build\NullableEnable.props = build\NullableEnable.props
build\ReactiveUI.props = build\ReactiveUI.props
build\ReferenceCoreLibraries.props = build\ReferenceCoreLibraries.props
build\Rx.props = build\Rx.props
build\SampleApp.props = build\SampleApp.props
build\SharedVersion.props = build\SharedVersion.props
build\SharpDX.props = build\SharpDX.props
build\SkiaSharp.props = build\SkiaSharp.props
build\SourceGenerators.props = build\SourceGenerators.props
build\SourceLink.props = build\SourceLink.props
@ -170,8 +158,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.X11", "src\Avaloni
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformSanityChecks", "samples\PlatformSanityChecks\PlatformSanityChecks.csproj", "{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.UnitTests", "tests\Avalonia.ReactiveUI.UnitTests\Avalonia.ReactiveUI.UnitTests.csproj", "{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Dialogs", "src\Avalonia.Dialogs\Avalonia.Dialogs.csproj", "{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.FreeDesktop", "src\Avalonia.FreeDesktop\Avalonia.FreeDesktop.csproj", "{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}"
@ -216,14 +202,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SingleProjectSandbox", "sam
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Browser", "src\Browser\Avalonia.Browser\Avalonia.Browser.csproj", "{4A39637C-9338-4925-A4DB-D072E292EC78}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Browser.Blazor", "src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj", "{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Browser.Blazor", "samples\ControlCatalog.Browser.Blazor\ControlCatalog.Browser.Blazor.csproj", "{90B08091-9BBD-4362-B712-E9F2CC62B218}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\ReactiveUIDemo\ReactiveUIDemo.csproj", "{75C47156-C5D8-44BC-A5A7-E8657C2248D6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
@ -269,16 +249,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.NUnit", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.NUnit.UnitTests", "tests\Avalonia.Headless.NUnit.UnitTests\Avalonia.Headless.NUnit.UnitTests.csproj", "{2999D79E-3C20-4A90-B651-CA7E0AC92D35}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.XUnit.UnitTests", "tests\Avalonia.Headless.XUnit.UnitTests\Avalonia.Headless.XUnit.UnitTests.csproj", "{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tizen", "Tizen", "{D1300000-7217-4693-8B0F-57CBD5814302}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Tizen", "src\Tizen\Avalonia.Tizen\Avalonia.Tizen.csproj", "{DFFBDBF5-5DBE-47ED-9EAE-D40B75AC99E8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Tizen", "samples\ControlCatalog.Tizen\ControlCatalog.Tizen.csproj", "{A0B29221-2B6F-4B29-A4D5-2227811B5915}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Metal", "src\Avalonia.Metal\Avalonia.Metal.csproj", "{60B4ED1F-ECFA-453B-8A70-1788261C8355}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Build.Tasks.UnitTest", "tests\Avalonia.Build.Tasks.UnitTest\Avalonia.Build.Tasks.UnitTest.csproj", "{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}"
@ -303,9 +273,19 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Automation",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XEmbedSample", "samples\XEmbedSample\XEmbedSample.csproj", "{255614F5-CB64-4ECA-A026-E0B1AF6A2EF4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.MacCatalyst", "samples\ControlCatalog.iOS\ControlCatalog.MacCatalyst.csproj", "{DE3C28DD-B602-4750-831D-345102A54CA0}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.MacCatalyst", "samples\ControlCatalog.MacCatalyst\ControlCatalog.MacCatalyst.csproj", "{DE3C28DD-B602-4750-831D-345102A54CA0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.tvOS", "samples\ControlCatalog.tvOS\ControlCatalog.tvOS.csproj", "{14342787-B4EF-4076-8C91-BA6C523DE8DF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.NUnit.PerAssembly.UnitTests", "tests\Avalonia.Headless.NUnit.PerAssembly.UnitTests\Avalonia.Headless.NUnit.PerAssembly.UnitTests.csproj", "{A175EFAE-476C-4DAA-87D5-742C18CFCC27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.NUnit.PerTest.UnitTests", "tests\Avalonia.Headless.NUnit.PerTest.UnitTests\Avalonia.Headless.NUnit.PerTest.UnitTests.csproj", "{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit.PerAssembly.UnitTests", "tests\Avalonia.Headless.XUnit.PerAssembly.UnitTests\Avalonia.Headless.XUnit.PerAssembly.UnitTests.csproj", "{342D2657-2F84-493C-B74B-9D2CAE5D9DAB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit.PerTest.UnitTests", "tests\Avalonia.Headless.XUnit.PerTest.UnitTests\Avalonia.Headless.XUnit.PerTest.UnitTests.csproj", "{26918642-829D-4FA2-B60A-BE8D83F4E063}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.tvOS", "samples\ControlCatalog.iOS\ControlCatalog.tvOS.csproj", "{14342787-B4EF-4076-8C91-BA6C523DE8DF}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.IntegrationTests.Win32", "tests\Avalonia.IntegrationTests.Win32\Avalonia.IntegrationTests.Win32.csproj", "{11522B0D-BF31-42D5-8FC5-41E58F319AF9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -321,10 +301,6 @@ Global
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|Any CPU.Build.0 = Debug|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|Any CPU.ActiveCfg = Release|Any CPU
{811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|Any CPU.Build.0 = Release|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|Any CPU.Build.0 = Release|Any CPU
{D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -345,14 +321,6 @@ Global
{2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|Any CPU.Build.0 = Release|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.Build.0 = Release|Any CPU
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|Any CPU.Build.0 = Release|Any CPU
{99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99135EAB-653D-47E4-A378-C96E1278CA44}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -361,10 +329,6 @@ Global
{3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|Any CPU.Build.0 = Release|Any CPU
{6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|Any CPU.Build.0 = Release|Any CPU
{6417E941-21BC-467B-A771-0DE389353CE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6417E941-21BC-467B-A771-0DE389353CE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6417E941-21BC-467B-A771-0DE389353CE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -431,10 +395,6 @@ Global
{7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|Any CPU.Build.0 = Release|Any CPU
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Any CPU.Build.0 = Release|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -497,10 +457,6 @@ Global
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|Any CPU.Build.0 = Release|Any CPU
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|Any CPU.Build.0 = Release|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -585,22 +541,10 @@ Global
{4A39637C-9338-4925-A4DB-D072E292EC78}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A39637C-9338-4925-A4DB-D072E292EC78}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A39637C-9338-4925-A4DB-D072E292EC78}.Release|Any CPU.Build.0 = Release|Any CPU
{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Release|Any CPU.Build.0 = Release|Any CPU
{15B93A4C-1B46-43F6-B534-7B25B6E99932}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{15B93A4C-1B46-43F6-B534-7B25B6E99932}.Debug|Any CPU.Build.0 = Debug|Any CPU
{15B93A4C-1B46-43F6-B534-7B25B6E99932}.Release|Any CPU.ActiveCfg = Release|Any CPU
{15B93A4C-1B46-43F6-B534-7B25B6E99932}.Release|Any CPU.Build.0 = Release|Any CPU
{90B08091-9BBD-4362-B712-E9F2CC62B218}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{90B08091-9BBD-4362-B712-E9F2CC62B218}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90B08091-9BBD-4362-B712-E9F2CC62B218}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90B08091-9BBD-4362-B712-E9F2CC62B218}.Release|Any CPU.Build.0 = Release|Any CPU
{75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Release|Any CPU.Build.0 = Release|Any CPU
{C810060E-3809-4B74-A125-F11533AF9C1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C810060E-3809-4B74-A125-F11533AF9C1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C810060E-3809-4B74-A125-F11533AF9C1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -657,24 +601,6 @@ Global
{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Release|Any CPU.Build.0 = Release|Any CPU
{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Release|Any CPU.Build.0 = Release|Any CPU
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Release|Any CPU.Build.0 = Release|Any CPU
{DFFBDBF5-5DBE-47ED-9EAE-D40B75AC99E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DFFBDBF5-5DBE-47ED-9EAE-D40B75AC99E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DFFBDBF5-5DBE-47ED-9EAE-D40B75AC99E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DFFBDBF5-5DBE-47ED-9EAE-D40B75AC99E8}.Release|Any CPU.Build.0 = Release|Any CPU
{A0B29221-2B6F-4B29-A4D5-2227811B5915}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0B29221-2B6F-4B29-A4D5-2227811B5915}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0B29221-2B6F-4B29-A4D5-2227811B5915}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{A0B29221-2B6F-4B29-A4D5-2227811B5915}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0B29221-2B6F-4B29-A4D5-2227811B5915}.Release|Any CPU.Build.0 = Release|Any CPU
{A0B29221-2B6F-4B29-A4D5-2227811B5915}.Release|Any CPU.Deploy.0 = Release|Any CPU
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -719,17 +645,34 @@ Global
{14342787-B4EF-4076-8C91-BA6C523DE8DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14342787-B4EF-4076-8C91-BA6C523DE8DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14342787-B4EF-4076-8C91-BA6C523DE8DF}.Release|Any CPU.Build.0 = Release|Any CPU
{A175EFAE-476C-4DAA-87D5-742C18CFCC27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A175EFAE-476C-4DAA-87D5-742C18CFCC27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A175EFAE-476C-4DAA-87D5-742C18CFCC27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A175EFAE-476C-4DAA-87D5-742C18CFCC27}.Release|Any CPU.Build.0 = Release|Any CPU
{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C}.Release|Any CPU.Build.0 = Release|Any CPU
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB}.Release|Any CPU.Build.0 = Release|Any CPU
{26918642-829D-4FA2-B60A-BE8D83F4E063}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26918642-829D-4FA2-B60A-BE8D83F4E063}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26918642-829D-4FA2-B60A-BE8D83F4E063}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26918642-829D-4FA2-B60A-BE8D83F4E063}.Release|Any CPU.Build.0 = Release|Any CPU
{11522B0D-BF31-42D5-8FC5-41E58F319AF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11522B0D-BF31-42D5-8FC5-41E58F319AF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11522B0D-BF31-42D5-8FC5-41E58F319AF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11522B0D-BF31-42D5-8FC5-41E58F319AF9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{811A76CF-1CF6-440F-963B-BBE31BD72A82} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{3E908F67-5543-4879-A1DC-08EACE79B3CD} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{5CCB5571-7C30-4E7D-967D-0E2158EBD91F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{2905FF23-53FB-45E6-AA49-6AF47A172056} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{DABFD304-D6A4-4752-8123-C2CCF7AC7831} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{99135EAB-653D-47E4-A378-C96E1278CA44} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{3E53A01A-B331-47F3-B828-4A5717E77A24} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{6417E941-21BC-467B-A771-0DE389353CE6} = {8B6A8209-894F-4BA1-B880-965FD453982C}
@ -748,7 +691,6 @@ Global
{F1FDC5B0-4654-416F-AE69-E3E9BBD87801} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{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}
{854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888}
{E1582370-37B3-403C-917F-8209551B1634} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
@ -760,7 +702,6 @@ Global
{3C471044-3640-45E3-B1B2-16D2FF8399EE} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C}
{41B02319-965D-4945-8005-C1A3D1224165} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
@ -778,10 +719,7 @@ Global
{1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{3B8519C1-2F51-4F12-A348-120AB91D4532} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{4A39637C-9338-4925-A4DB-D072E292EC78} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
{47F8530C-F19B-4B1A-B4D6-EB231522AE5D} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
{15B93A4C-1B46-43F6-B534-7B25B6E99932} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{90B08091-9BBD-4362-B712-E9F2CC62B218} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
@ -794,10 +732,6 @@ Global
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
{ED976634-B118-43F8-8B26-0279C7A7044F} = {FF237916-7150-496B-89ED-6CA3292896E7}
{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{2999D79E-3C20-4A90-B651-CA7E0AC92D35} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{DFFBDBF5-5DBE-47ED-9EAE-D40B75AC99E8} = {D1300000-7217-4693-8B0F-57CBD5814302}
{A0B29221-2B6F-4B29-A4D5-2227811B5915} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{9D6AEF22-221F-4F4B-B335-A4BA510F002C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{5BF0C3B8-E595-4940-AB30-2DA206C2F085} = {9D6AEF22-221F-4F4B-B335-A4BA510F002C}
@ -810,6 +744,11 @@ Global
{255614F5-CB64-4ECA-A026-E0B1AF6A2EF4} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{DE3C28DD-B602-4750-831D-345102A54CA0} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{14342787-B4EF-4076-8C91-BA6C523DE8DF} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{A175EFAE-476C-4DAA-87D5-742C18CFCC27} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{26918642-829D-4FA2-B60A-BE8D83F4E063} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{11522B0D-BF31-42D5-8FC5-41E58F319AF9} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

4
CONTRIBUTING.md

@ -10,7 +10,7 @@ A bug fix will ideally be accompanied by tests. There are a few types of tests:
- Unit tests are for issues that aren't related to platform features. These tests are located in the `tests` directly, categorised by the assembly which they're testing.
- Integration tests are for issues that are related to platform features (for example fixing a bug with Window resizing). These tests are located in the `tests/Avalonia.IntegrationTests.Appium` directory. Integration tests should be run on Windows and macOS. See the readme in that directory for more information
- Render tests are for issues with rendering. These tests are located in `tests/Avalonia.RenderTests` with separate project files for Skia and Direct2D. The Direct2D backend is currently mostly unmaintained so render tests that just run on Skia are acceptable
- Render tests are for issues with rendering. These tests are located in `tests/Avalonia.RenderTests` and are imported in the `Avalonia.Skia.RenderTests` project.
It's not always feasible to accompany a bug fix with a test, but doing so will speed up the review process.
@ -88,4 +88,4 @@ Render tests should describe what the produced image is:
## Code of Conduct
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
For more information see the [Contributor Covenant Code of Conduct](https://dotnetfoundation.org/code-of-conduct)
For more information see the [Contributor Covenant Code of Conduct](https://dotnetfoundation.org/code-of-conduct)

4
Directory.Build.props

@ -4,11 +4,11 @@
<Import Project="$(MSBuildThisFileDirectory)/build/WarnAsErrors.props" />
<PropertyGroup>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netstandard2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\$(AvsCurrentTargetFramework)\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<!-- https://github.com/dotnet/msbuild/issues/2661 -->
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies>
<RunApiCompat>False</RunApiCompat>
<LangVersion>12</LangVersion>
<LangVersion>14.0</LangVersion>
<CreateHardLinksForCopyAdditionalFilesIfPossible>true</CreateHardLinksForCopyAdditionalFilesIfPossible>
<CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>true</CreateHardLinksForCopyFilesToOutputDirectoryIfPossible>
<CreateHardLinksForCopyLocalIfPossible>true</CreateHardLinksForCopyLocalIfPossible>

16
NuGet.Config

@ -3,7 +3,19 @@
<configuration>
<packageSources>
<clear />
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="skiasharp" value="https://aka.ms/skiasharp-eap/index.json" />
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="azure-dotnet10-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10-transport/nuget/v3/index.json" />
</packageSources>
<packageSourceMapping>
<clear />
</packageSourceMapping>
<auditSources>
<clear />
</auditSources>
<disabledPackageSources>
<clear />
</disabledPackageSources>
<fallbackPackageFolders>
<clear />
</fallbackPackageFolders>
</configuration>

22
api/Avalonia.Android.nupkg.xml

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Android.AvaloniaMainActivity`1</Target>
<Left>baseline/net8.0-android34.0/Avalonia.Android.dll</Left>
<Right>target/net8.0-android34.0/Avalonia.Android.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Android.AndroidViewControlHandle.get_HandleDescriptor</Target>
<Left>baseline/net8.0-android34.0/Avalonia.Android.dll</Left>
<Right>target/net8.0-android34.0/Avalonia.Android.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0007</DiagnosticId>
<Target>T:Avalonia.Android.AndroidViewControlHandle</Target>
<Left>baseline/net8.0-android34.0/Avalonia.Android.dll</Left>
<Right>target/net8.0-android34.0/Avalonia.Android.dll</Right>
</Suppression>
</Suppressions>

22
api/Avalonia.Browser.nupkg.xml

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Browser.JSObjectControlHandle.get_Handle</Target>
<Left>baseline/net8.0-browser1.0/Avalonia.Browser.dll</Left>
<Right>target/net8.0-browser1.0/Avalonia.Browser.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Browser.JSObjectControlHandle.get_HandleDescriptor</Target>
<Left>baseline/net8.0-browser1.0/Avalonia.Browser.dll</Left>
<Right>target/net8.0-browser1.0/Avalonia.Browser.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0007</DiagnosticId>
<Target>T:Avalonia.Browser.JSObjectControlHandle</Target>
<Left>baseline/net8.0-browser1.0/Avalonia.Browser.dll</Left>
<Right>target/net8.0-browser1.0/Avalonia.Browser.dll</Right>
</Suppression>
</Suppressions>

10
api/Avalonia.FreeDesktop.nupkg.xml

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Tmds.DBus.SourceGenerator.PropertyChanges`1</Target>
<Left>baseline/netstandard2.0/Avalonia.FreeDesktop.dll</Left>
<Right>target/netstandard2.0/Avalonia.FreeDesktop.dll</Right>
</Suppression>
</Suppressions>

16
api/Avalonia.Skia.nupkg.xml

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Skia.SkiaSharpExtensions.ToSKFilterQuality(Avalonia.Media.Imaging.BitmapInterpolationMode)</Target>
<Left>baseline/netstandard2.0/Avalonia.Skia.dll</Left>
<Right>target/netstandard2.0/Avalonia.Skia.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Skia.ISkiaGpuWithPlatformGraphicsContext.TryGetGrContext</Target>
<Left>baseline/netstandard2.0/Avalonia.Skia.dll</Left>
<Right>target/netstandard2.0/Avalonia.Skia.dll</Right>
</Suppression>
</Suppressions>

16
api/Avalonia.Themes.Fluent.nupkg.xml

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0007</DiagnosticId>
<Target>T:Avalonia.Themes.Fluent.ColorPaletteResources</Target>
<Left>baseline/netstandard2.0/Avalonia.Themes.Fluent.dll</Left>
<Right>target/netstandard2.0/Avalonia.Themes.Fluent.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0008</DiagnosticId>
<Target>T:Avalonia.Themes.Fluent.ColorPaletteResources</Target>
<Left>baseline/netstandard2.0/Avalonia.Themes.Fluent.dll</Left>
<Right>target/netstandard2.0/Avalonia.Themes.Fluent.dll</Right>
</Suppression>
</Suppressions>

214
api/Avalonia.Win32.nupkg.xml

@ -1,214 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.DockPosition</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IDockProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IExpandCollapseProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IGridItemProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IGridProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IInvokeProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IMultipleViewProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IRangeValueProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IRawElementProviderAdviseEvents</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IRawElementProviderFragment</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IRawElementProviderFragmentRoot</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IRawElementProviderSimple</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IRawElementProviderSimple2</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IScrollItemProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IScrollProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.ISelectionItemProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.ISelectionProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.ISynchronizedInputProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.ITableItemProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.ITableProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.ITextProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.ITextRangeProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IToggleProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.ITransformProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IValueProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.IWindowProvider</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.NavigateDirection</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.ProviderOptions</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.RowOrColumnMajor</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.SupportedTextSelection</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.SynchronizedInputType</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.TextPatternRangeEndpoint</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.TextUnit</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.WindowInteractionState</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Win32.Interop.Automation.WindowVisualState</Target>
<Left>baseline/netstandard2.0/Avalonia.Win32.dll</Left>
<Right>target/netstandard2.0/Avalonia.Win32.dll</Right>
</Suppression>
</Suppressions>

16
api/Avalonia.iOS.nupkg.xml

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.iOS.UIViewControlHandle.get_HandleDescriptor</Target>
<Left>baseline/net8.0-tvos17.0/Avalonia.iOS.dll</Left>
<Right>target/net8.0-tvos17.0/Avalonia.iOS.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0007</DiagnosticId>
<Target>T:Avalonia.iOS.UIViewControlHandle</Target>
<Left>baseline/net8.0-tvos17.0/Avalonia.iOS.dll</Left>
<Right>target/net8.0-tvos17.0/Avalonia.iOS.dll</Right>
</Suppression>
</Suppressions>

272
api/Avalonia.nupkg.xml

@ -1,272 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Controls.PseudolassesExtensions</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Data.Core.CastTypePropertyPathElement</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Data.Core.ChildTraversalPropertyPathElement</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Data.Core.EnsureTypePropertyPathElement</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Data.Core.IPropertyPathElement</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Data.Core.PropertyPath</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Data.Core.PropertyPathBuilder</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Data.Core.PropertyPropertyPathElement</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Utilities.CharacterReader</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Utilities.IdentifierParser</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Utilities.KeywordParser</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Avalonia.Utilities.StyleClassParser</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Diagnostics.AppliedStyle.get_HasActivator</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Diagnostics.AppliedStyle.get_IsActive</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Diagnostics.AppliedStyle.get_Style</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Diagnostics.StyledElementExtensions.GetStyleDiagnostics(Avalonia.StyledElement)</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Diagnostics.StyleDiagnostics.#ctor(System.Collections.Generic.IReadOnlyList{Avalonia.Diagnostics.AppliedStyle})</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Diagnostics.StyleDiagnostics.get_AppliedStyles</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Target>M:Avalonia.Threading.DispatcherPriorityAwaitable.get_IsCompleted</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Threading.DispatcherPriorityAwaitable.GetAwaiter</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Threading.DispatcherPriorityAwaitable.GetResult</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Threading.DispatcherPriorityAwaitable.OnCompleted(System.Action)</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
<Target>M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Threading.DispatcherPriorityAwaitable`1.GetAwaiter</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Threading.DispatcherPriorityAwaitable`1.GetResult</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.IPopupHost.ConfigurePosition(Avalonia.Visual,Avalonia.Controls.PlacementMode,Avalonia.Point,Avalonia.Controls.Primitives.PopupPositioning.PopupAnchor,Avalonia.Controls.Primitives.PopupPositioning.PopupGravity,Avalonia.Controls.Primitives.PopupPositioning.PopupPositionerConstraintAdjustment,System.Nullable{Avalonia.Rect})</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
<Target>M:Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Avalonia.Controls.Screens.#ctor(Avalonia.Platform.IScreenImpl)</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Input.Platform.IClipboard.FlushAsync</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Input.Platform.IClipboard.TryGetInProcessDataObjectAsync</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.Storage.IStorageFolder.GetFileAsync(System.String)</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Platform.Storage.IStorageFolder.GetFolderAsync(System.String)</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Controls.Notifications.IManagedNotificationManager.Close(System.Object)</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Controls.Notifications.INotificationManager.Close(Avalonia.Controls.Notifications.INotification)</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Controls.Notifications.INotificationManager.CloseAll</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.IPopupHost.ConfigurePosition(Avalonia.Controls.Primitives.PopupPositioning.PopupPositionRequest)</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Avalonia.Controls.Primitives.IPopupHost.TakeFocus</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
<Target>M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant)</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Controls.Platform.IInsetsManager.DisplayEdgeToEdgePreference</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
<Target>M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(System.Nullable{Avalonia.Platform.PlatformThemeVariant})</Target>
<Left>baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net10.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>P:Avalonia.Controls.Platform.IInsetsManager.DisplaysEdgeToEdge</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0007</DiagnosticId>
<Target>T:Avalonia.Threading.DispatcherPriorityAwaitable</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0007</DiagnosticId>
<Target>T:Avalonia.Threading.DispatcherPriorityAwaitable`1</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0008</DiagnosticId>
<Target>T:Avalonia.Threading.DispatcherPriorityAwaitable</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0008</DiagnosticId>
<Target>T:Avalonia.Threading.DispatcherPriorityAwaitable`1</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Diagnostics.StyleDiagnostics</Target>
<Left>baseline/netstandard2.0/Avalonia.Base.dll</Left>
<Right>target/netstandard2.0/Avalonia.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0009</DiagnosticId>
<Target>T:Avalonia.Controls.Screens</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0012</DiagnosticId>
<Target>M:Avalonia.Controls.Button.OnAccessKey(Avalonia.Interactivity.RoutedEventArgs)</Target>
<Left>baseline/netstandard2.0/Avalonia.Controls.dll</Left>
<Right>target/netstandard2.0/Avalonia.Controls.dll</Right>
<Target>M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(System.Nullable{Avalonia.Platform.PlatformThemeVariant})</Target>
<Left>baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll</Left>
<Right>current/Avalonia/lib/net8.0/Avalonia.Controls.dll</Right>
</Suppression>
</Suppressions>
</Suppressions>

28
azure-pipelines-integrationtests.yml

@ -5,13 +5,13 @@ jobs:
steps:
- task: UseDotNet@2
displayName: 'Use .NET 6.0 Runtime'
displayName: 'Use .NET 8.0 Runtime'
inputs:
packageType: runtime
version: 6.0.x
version: 8.0.x
- task: UseDotNet@2
displayName: 'Use .NET 8.0 SDK'
displayName: 'Use .NET 10.0 SDK'
inputs:
packageType: sdk
useGlobalJson: true
@ -25,16 +25,16 @@ jobs:
arch="arm64"
fi
git clean -ffdx
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
pkill node
pkill testmanagerd
appium > appium.out &
pkill IntegrationTestApp
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
./build.sh CompileNative
rm -rf $(osascript -e "POSIX path of (path to application id \"net.avaloniaui.avalonia.integrationtestapp\")")
pkill IntegrationTestApp
./samples/IntegrationTestApp/bundle.sh
open -n ./samples/IntegrationTestApp/bin/Debug/net8.0/osx-$arch/publish/IntegrationTestApp.app
open -n ./samples/IntegrationTestApp/bin/Debug/net10.0/osx-$arch/publish/IntegrationTestApp.app
pkill IntegrationTestApp
displayName: 'Build IntegrationTestApp'
@ -56,17 +56,17 @@ jobs:
- job: Windows
pool:
vmImage: 'windows-2022'
vmImage: 'windows-2025'
steps:
- task: UseDotNet@2
displayName: 'Use .NET 6.0 Runtime'
displayName: 'Use .NET 8.0 Runtime'
inputs:
packageType: runtime
version: 6.0.x
version: 8.0.x
- task: UseDotNet@2
displayName: 'Use .NET 8.0 SDK'
displayName: 'Use .NET 10.0 SDK'
inputs:
packageType: sdk
useGlobalJson: true
@ -87,10 +87,16 @@ jobs:
displayName: 'Build test project'
inputs:
command: 'build'
projects: 'tests\Avalonia.IntegrationTests.Appium\Avalonia.IntegrationTests.Appium.csproj'
projects: 'tests/Avalonia.IntegrationTests.Appium/Avalonia.IntegrationTests.Appium.csproj'
- task: DotNetCoreCLI@2
displayName: 'Run Win32 Integration Tests'
inputs:
command: 'run'
projects: 'tests/Avalonia.IntegrationTests.Win32/Avalonia.IntegrationTests.Win32.csproj'
- task: VSTest@2
displayName: 'Run Integration Tests'
displayName: 'Run Appium Integration Tests'
inputs:
testAssemblyVer2: '**\bin\**\Avalonia.IntegrationTests.Appium.dll'
runSettingsFile: 'tests\Avalonia.IntegrationTests.Appium\record-video.runsettings'

34
azure-pipelines.yml

@ -2,12 +2,12 @@ jobs:
- job: GetPRNumber
pool:
vmImage: 'ubuntu-22.04'
vmImage: 'ubuntu-24.04'
variables:
SolutionDir: '$(Build.SourcesDirectory)'
steps:
- task: UseDotNet@2
displayName: 'Use .NET 8.0 SDK'
displayName: 'Use .NET 10.0 SDK'
inputs:
packageType: sdk
useGlobalJson: true
@ -23,16 +23,16 @@ jobs:
- job: Linux
pool:
vmImage: 'ubuntu-22.04'
vmImage: 'ubuntu-24.04'
steps:
- task: UseDotNet@2
displayName: 'Use .NET 6.0 Runtime'
displayName: 'Use .NET 8.0 Runtime'
inputs:
packageType: runtime
version: 6.0.x
version: 8.0.x
- task: UseDotNet@2
displayName: 'Use .NET 8.0 SDK'
displayName: 'Use .NET 10.0 SDK'
inputs:
packageType: sdk
useGlobalJson: true
@ -62,16 +62,16 @@ jobs:
variables:
SolutionDir: '$(Build.SourcesDirectory)'
pool:
vmImage: 'macos-13'
vmImage: 'macos-15'
steps:
- task: UseDotNet@2
displayName: 'Use .NET 6.0 Runtime'
displayName: 'Use .NET 8.0 Runtime'
inputs:
packageType: runtime
version: 6.0.x
version: 8.0.x
- task: UseDotNet@2
displayName: 'Use .NET 8.0 SDK'
displayName: 'Use .NET 10.0 SDK'
inputs:
packageType: sdk
useGlobalJson: true
@ -95,11 +95,11 @@ jobs:
inputs:
actions: 'build'
scheme: ''
sdk: 'macosx13.0'
sdk: 'macosx26.0'
configuration: 'Release'
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
xcodeVersion: 'specifyPath' # Options: 8, 9, default, specifyPath
xcodeDeveloperDir: '/Applications/Xcode_14.1.app/Contents/Developer'
xcodeDeveloperDir: '/Applications/Xcode_26.0.app/Contents/Developer'
args: '-derivedDataPath ./'
- task: CmdLine@2
@ -134,18 +134,18 @@ jobs:
- job: Windows
pool:
vmImage: 'windows-2022'
vmImage: 'windows-2025'
variables:
SolutionDir: '$(Build.SourcesDirectory)'
steps:
- task: UseDotNet@2
displayName: 'Use .NET 6.0 Runtime'
displayName: 'Use .NET 8.0 Runtime'
inputs:
packageType: runtime
version: 6.0.x
version: 8.0.x
- task: UseDotNet@2
displayName: 'Use .NET 8.0 SDK'
displayName: 'Use .NET 10.0 SDK'
inputs:
packageType: sdk
useGlobalJson: true
@ -160,7 +160,7 @@ jobs:
displayName: 'Install Nuke'
inputs:
script: |
dotnet tool install --global Nuke.GlobalTool --version 6.2.1
dotnet tool install --global Nuke.GlobalTool --version 9.0.4
- task: CmdLine@2
displayName: 'Run Nuke'

2
build/AnalyzerProject.targets

@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all"/>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" PrivateAssets="all"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.5.0" PrivateAssets="all" />
</ItemGroup>

12
build/Base.props

@ -1,12 +1,8 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- '!NET6_0_OR_GREATER' equivalent -->
<ItemGroup Condition="!('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '6.0')))">
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>
<ItemGroup Condition="!('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '6.0')))">
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
</ItemGroup>
<!-- '!NET8_0_OR_GREATER' equivalent -->
<ItemGroup Condition="!('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '8.0')))">
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.10" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="9.0.10" />
</ItemGroup>
</Project>

2
build/DevSingleProject.targets

@ -9,8 +9,6 @@
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\src\Android\Avalonia.Android\Avalonia.Android.csproj" Condition="'$(_AvaloniaAndroidTarget)' == 'true'" />
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\src\iOS\Avalonia.iOS\Avalonia.iOS.csproj" Condition="'$(_AvaloniaIOSTarget)' == 'true'" />
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\src\Browser\Avalonia.Browser\Avalonia.Browser.csproj" Condition="'$(_AvaloniaBrowserTarget)' == 'true'" />
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\src\Tizen\Avalonia.Tizen\Avalonia.Tizen.csproj" Condition="'$(_AvaloniaTizenTarget)' == 'true'" />
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\src\Tizen\Avalonia.Tizen\Avalonia.Tizen.csproj" Condition="'$(_AvaloniaTizenTarget)' == 'true'" />
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" Condition="'$(_AvaloniaDesktopTarget)' == 'true'" />
</ItemGroup>

6
build/HarfBuzzSharp.props

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

2
build/ImageSharp.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.10" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.12" />
</ItemGroup>
</Project>

5
build/JetBrains.dotMemoryUnit.props

@ -1,5 +0,0 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="JetBrains.DotMemoryUnit" Version="3.2.20220510" />
</ItemGroup>
</Project>

2
build/Microsoft.Reactive.Testing.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="Microsoft.Reactive.Testing" Version="4.1.6" />
<PackageReference Include="Microsoft.Reactive.Testing" Version="6.1.0" />
</ItemGroup>
</Project>

2
build/Moq.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="Moq" Version="4.20.72" />
</ItemGroup>
</Project>

5
build/ReactiveUI.props

@ -1,5 +0,0 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="ReactiveUI" Version="20.1.1" />
</ItemGroup>
</Project>

2
build/Rx.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="System.Reactive" Version="6.0.1" />
<PackageReference Include="System.Reactive" Version="6.1.0" />
</ItemGroup>
</Project>

1
build/SharedVersion.props

@ -3,7 +3,6 @@
<PropertyGroup>
<Product>Avalonia</Product>
<Version>12.0.999</Version>
<ApiCompatVersion>11.1.0</ApiCompatVersion>
<Authors>Avalonia Team</Authors>
<Copyright>Copyright 2013-$([System.DateTime]::Now.ToString(`yyyy`)) &#169; The AvaloniaUI Project</Copyright>
<PackageProjectUrl>https://avaloniaui.net/?utm_source=nuget&amp;utm_medium=referral&amp;utm_content=project_homepage_link</PackageProjectUrl>

6
build/SkiaSharp.props

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

4
build/SourceLink.props

@ -14,10 +14,6 @@
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
</ItemGroup>
<!-- Workaround for https://github.com/dotnet/sdk/issues/11105 -->
<ItemGroup>
<SourceRoot Include="$([MSBuild]::EnsureTrailingSlash($(NuGetPackageRoot)))" Condition="'$(NuGetPackageRoot)' != ''" />

19
build/TargetFrameworks.props

@ -1,19 +1,17 @@
<Project>
<PropertyGroup>
<AvsCurrentTargetFramework>net8.0</AvsCurrentTargetFramework>
<AvsCurrentTargetFramework>net10.0</AvsCurrentTargetFramework>
<AvsCurrentWindowsTargetFramework>$(AvsCurrentTargetFramework)-windows</AvsCurrentWindowsTargetFramework>
<AvsCurrentMacOSTargetFramework>$(AvsCurrentTargetFramework)-macos</AvsCurrentMacOSTargetFramework>
<AvsCurrentAndroidTargetFramework>$(AvsCurrentTargetFramework)-android34.0</AvsCurrentAndroidTargetFramework>
<AvsCurrentMacCatalystTargetFramework>$(AvsCurrentTargetFramework)-maccatalyst17.0</AvsCurrentMacCatalystTargetFramework>
<AvsCurrentIOSTargetFramework>$(AvsCurrentTargetFramework)-ios17.0</AvsCurrentIOSTargetFramework>
<AvsCurrentTvOSTargetFramework>$(AvsCurrentTargetFramework)-tvos17.0</AvsCurrentTvOSTargetFramework>
<AvsCurrentAndroidTargetFramework>$(AvsCurrentTargetFramework)-android36.0</AvsCurrentAndroidTargetFramework>
<AvsCurrentMacCatalystTargetFramework>$(AvsCurrentTargetFramework)-maccatalyst26.0</AvsCurrentMacCatalystTargetFramework>
<AvsCurrentIOSTargetFramework>$(AvsCurrentTargetFramework)-ios26.0</AvsCurrentIOSTargetFramework>
<AvsCurrentTvOSTargetFramework>$(AvsCurrentTargetFramework)-tvos26.0</AvsCurrentTvOSTargetFramework>
<AvsCurrentBrowserTargetFramework>$(AvsCurrentTargetFramework)-browser</AvsCurrentBrowserTargetFramework>
<AvsCurrentTizenTargetFramework>$(AvsCurrentTargetFramework)-tizen</AvsCurrentTizenTargetFramework>
<AvsCurrentTizenTargetSdk>8.0.155</AvsCurrentTizenTargetSdk>
</PropertyGroup>
<PropertyGroup Condition="'$(AvsSkipBuildingLegacyTargetFrameworks)' != 'True'">
<AvsLegacyTargetFrameworks>net6.0</AvsLegacyTargetFrameworks>
<AvsLegacyWindowsTargetFrameworks>net6.0-windows</AvsLegacyWindowsTargetFrameworks>
<AvsLegacyTargetFrameworks>net8.0</AvsLegacyTargetFrameworks>
<AvsLegacyWindowsTargetFrameworks>net8.0-windows</AvsLegacyWindowsTargetFrameworks>
</PropertyGroup>
<PropertyGroup>
@ -22,8 +20,7 @@
<AvsMinSupportedIOSVersion>13.0</AvsMinSupportedIOSVersion>
<AvsMinSupportedTvOSVersion>13.0</AvsMinSupportedTvOSVersion>
<AvsMinSupportedMacCatalystVersion>13.1</AvsMinSupportedMacCatalystVersion>
<AvsMinSupportedTizenVersion>6.5</AvsMinSupportedTizenVersion>
<AvsMinSupportedAndroidVersion>21.0</AvsMinSupportedAndroidVersion>
<AvsMinSupportedAndroidVersion>24.0</AvsMinSupportedAndroidVersion>
<!-- Desktop OS min version is not set in any of ours backends, but only used in some samples. -->
<!-- Avalonia technically supports 10.12. -->
<AvsMinSupportedMacOsVersion>10.15</AvsMinSupportedMacOsVersion>

5
build/UnitTests.NetCore.targets

@ -3,7 +3,4 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
</ItemGroup>
</Project>
</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.9.2" />
<PackageReference Include="xunit.assert" Version="2.9.2" />
<PackageReference Include="xunit.core" Version="2.9.2" />
<PackageReference Include="xunit.extensibility.core" Version="2.9.2" />
<PackageReference Include="xunit.extensibility.execution" Version="2.9.2" />
<PackageReference Include="xunit.runner.console" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" Condition="'$(TargetFramework)' != 'netstandard2.0'" />
<PackageReference Include="Xunit.SkippableFact" Version="1.4.13" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.assert" Version="2.9.3" />
<PackageReference Include="xunit.core" Version="2.9.3" />
<PackageReference Include="xunit.extensibility.core" Version="2.9.3" />
<PackageReference Include="xunit.extensibility.execution" Version="2.9.3" />
<PackageReference Include="xunit.runner.console" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" Condition="'$(TargetFramework)' != 'netstandard2.0'" />
<PackageReference Include="Xunit.SkippableFact" Version="1.5.23" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
</ItemGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\avalonia.snk</AssemblyOriginatorKeyFile>

10
dirs.proj

@ -9,23 +9,17 @@
<ProjectReference Remove="**/*.shproj" />
<!-- Exclude iOS, Android and Browser samples from build -->
<ProjectReference Remove="samples/*.iOS/*.csproj" />
<ProjectReference Remove="samples/*.tvOS/*.csproj" />
<ProjectReference Remove="samples/*.MacCatalyst/*.csproj" />
<ProjectReference Remove="samples/*.Android/*.csproj" />
<ProjectReference Remove="samples/*.Browser/*.csproj" />
<ProjectReference Remove="samples/*.Blazor/*.csproj" />
<ProjectReference Remove="samples/*.Tizen/*.csproj" />
<ProjectReference Remove="samples/SingleProjectSandbox/*.csproj" />
<ProjectReference Remove="samples/ControlCatalog.Desktop/*.*proj" />
</ItemGroup>
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows'))">
<!-- Build mobile backends only on Windows, where we have installed android workload -->
<ProjectReference Remove="src/Android/**/*.*proj" />
<ProjectReference Remove="src/iOS/**/*.*proj" />
<ProjectReference Remove="src/Tizen/**/*.*proj" />
</ItemGroup>
<ItemGroup Condition="'$(SkipObscurePlatforms)' == 'True'">
<ProjectReference Remove="**/*Tizen/**/*.*proj" />
</ItemGroup>
<ItemGroup>

2
docs/api-compat.md

@ -29,4 +29,4 @@ API changes are validated against a **baseline version**—the reference point f
## Additional Resources
- [API Validation Tool Implementation](https://github.com/AvaloniaUI/Avalonia/pull/12072) - Pull request that introduced this feature
- [API Validation Tool Implementation](https://github.com/AvaloniaUI/Avalonia/pull/12072) - Pull request that introduced this feature

9
docs/build.md

@ -33,7 +33,7 @@ curl -sSL https://raw.githubusercontent.com/Samsung/Tizen.NET/main/workload/scri
## Build and Run Avalonia
```bash
cd samples\ControlCatalog.NetCore
cd samples\ControlCatalog.Desktop
dotnet restore
dotnet run
```
@ -47,7 +47,7 @@ If you want to open Avalonia in Visual Studio you have two options:
Avalonia requires Visual Studio 2022 or newer. The free Visual Studio Community edition works fine.
Build and run `ControlCatalog.NetCore` project to see the sample application.
Build and run `ControlCatalog.Desktop` project to see the sample application.
### Visual Studio Troubleshooting
@ -71,6 +71,9 @@ And run tests:
Or if you need to create nuget packages as well (it will compile and run tests automatically):
`nuke --target Package --configuration Release`
Alternatively, you can run nuke build direclty without installing Nuke global tool:
`dotnet run --project nukebuild/_build.csproj -- --configuration Debug`
# Linux/macOS
It's *not* possible to build the *whole* project on Linux/macOS. You can only build the subset targeting .NET Standard and .NET Core (which is, however, sufficient to get UI working on Linux/macOS). If you want to something that involves changing platform-specific APIs you'll need a Windows machine.
@ -108,4 +111,4 @@ It is possible to run some .NET Framework samples and tests using .NET Framework
## Building Avalonia into a local NuGet cache
See [Building Local NuGet Packages](nuget.md)
See [Building Local NuGet Packages](nuget.md)

2
docs/porting-code-from-3rd-party-sources.md

@ -9,4 +9,4 @@ When porting code or adapting code from other projects with a MIT compatible lic
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
```
If the file is a port of a specific commit of file from a 3rd party source, then consider including a permalink to the source file.
If the file is a port of a specific commit of file from a 3rd party source, then consider including a permalink to the source file.

2
external/XamlX

@ -1 +1 @@
Subproject commit 83567b8a50bbf612a0b1420a3dc6d8e8ebee2399
Subproject commit c32d3040e536ae9768233ea5a445697632578bd0

2
global.json

@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.411",
"version": "10.0.101",
"rollForward": "latestFeature"
},
"msbuild-sdks": {

3
native/Avalonia.Native/inc/com.h

@ -28,7 +28,8 @@ typedef DWORD ULONG;
#define E_UNEXPECTED 0x8000FFFFL
#define E_HANDLE 0x80070006L
#define E_INVALIDARG 0x80070057L
#define COR_E_INVALIDOPERATION 0x80131509L
#define COR_E_INVALIDOPERATION 0x80131509L
#define COR_E_OBJECTDISPOSED 0x80131622L
struct IUnknown
{

5
native/Avalonia.Native/inc/noarc.h

@ -8,4 +8,7 @@ public:
~CppAutoreleasePool();
};
#define START_ARP_CALL CppAutoreleasePool __autoreleasePool
#define START_ARP_CALL CppAutoreleasePool __autoreleasePool
extern void ReleaseNSObject(void* obj);
extern void RetainNSObject(void* obj);
extern uint64_t GetRetainCountForNSObject(void* obj);

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

@ -31,6 +31,8 @@
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; };
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
1A465D10246AB61600C5858B /* dnd.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A465D0F246AB61600C5858B /* dnd.mm */; };
1AC7F1432DCA0C2E003A161B /* crapium.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AC7F1422DCA0C2E003A161B /* crapium.mm */; };
1AE55B8C2DC1060E00FD0BB3 /* memhelp.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AE55B8B2DC1060E00FD0BB3 /* memhelp.mm */; };
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.mm */; };
37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; };
37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
@ -63,6 +65,7 @@
EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */ = {isa = PBXBuildFile; fileRef = EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */; };
F10084842BFF1F9E0024303E /* TopLevelImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = F10084832BFF1F9E0024303E /* TopLevelImpl.h */; };
F10084862BFF1FB40024303E /* TopLevelImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = F10084852BFF1FB40024303E /* TopLevelImpl.mm */; };
F931F8682E2D43A7004E081E /* clipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = F931F8672E2D43A4004E081E /* clipboard.h */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -90,6 +93,9 @@
1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = "<group>"; };
1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
1A465D0F246AB61600C5858B /* dnd.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = dnd.mm; sourceTree = "<group>"; };
1AC7F1422DCA0C2E003A161B /* crapium.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objcpp; path = crapium.mm; sourceTree = "<group>"; };
1AC7F1442DCA0D6A003A161B /* crapium.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = crapium.h; sourceTree = "<group>"; };
1AE55B8B2DC1060E00FD0BB3 /* memhelp.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = memhelp.mm; sourceTree = "<group>"; };
1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = "<group>"; };
37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = "<group>"; };
379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
@ -130,6 +136,7 @@
EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformSettings.mm; sourceTree = "<group>"; };
F10084832BFF1F9E0024303E /* TopLevelImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopLevelImpl.h; sourceTree = "<group>"; };
F10084852BFF1FB40024303E /* TopLevelImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TopLevelImpl.mm; sourceTree = "<group>"; };
F931F8672E2D43A4004E081E /* clipboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = clipboard.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -169,6 +176,9 @@
AB7A61E62147C814003C5833 = {
isa = PBXGroup;
children = (
1AC7F1442DCA0D6A003A161B /* crapium.h */,
1AC7F1422DCA0C2E003A161B /* crapium.mm */,
1AE55B8B2DC1060E00FD0BB3 /* memhelp.mm */,
F10084852BFF1FB40024303E /* TopLevelImpl.mm */,
BC7C33832C066F1100945A48 /* AvnAccessibility.h */,
BC7C33812C066DBF00945A48 /* AvnAutomationNode.h */,
@ -189,6 +199,7 @@
1AFD334023E03C4F0042899B /* controlhost.mm */,
5BF943652167AD1D009CAE35 /* cursor.h */,
5B21A981216530F500CEE36E /* cursor.mm */,
F931F8672E2D43A4004E081E /* clipboard.h */,
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */,
1A465D0F246AB61600C5858B /* dnd.mm */,
AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */,
@ -246,6 +257,7 @@
files = (
37155CE4233C00EB0034DCE9 /* menu.h in Headers */,
F10084842BFF1F9E0024303E /* TopLevelImpl.h in Headers */,
F931F8682E2D43A7004E081E /* clipboard.h in Headers */,
BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */,
183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */,
1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */,
@ -329,6 +341,7 @@
523484CA26EA688F00EA0C2C /* trayicon.mm in Sources */,
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */,
1AE55B8C2DC1060E00FD0BB3 /* memhelp.mm in Sources */,
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */,
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */,
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
@ -349,6 +362,7 @@
ED754D262A97306B0078B4DF /* PlatformRenderTimer.mm in Sources */,
1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */,
18391AC16726CBC45856233B /* AvnWindow.mm in Sources */,
1AC7F1432DCA0C2E003A161B /* crapium.mm in Sources */,
18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */,
EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */,
64B1EA48E308E574685AFB07 /* metal.mm in Sources */,
@ -471,6 +485,7 @@
DYLIB_CURRENT_VERSION = 1;
EXECUTABLE_PREFIX = lib;
HEADER_SEARCH_PATHS = ../../inc;
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
@ -482,6 +497,7 @@
DYLIB_CURRENT_VERSION = 1;
EXECUTABLE_PREFIX = lib;
HEADER_SEARCH_PATHS = ../../inc;
MACOSX_DEPLOYMENT_TARGET = 10.13;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;

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

@ -19,7 +19,6 @@
-(AvnPoint) translateLocalPoint:(AvnPoint)pt;
-(void) onClosed;
-(void) setModifiers:(NSEventModifierFlags)modifierFlags;
-(void) resetPressedMouseButtons;
-(AvnPlatformResizeReason) getResizeReason;
-(void) setResizeReason:(AvnPlatformResizeReason)reason;

25
native/Avalonia.Native/src/OSX/AvnView.mm

@ -456,13 +456,12 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt)
switch(event.buttonNumber)
{
case 2:
case 3:
[self mouseEvent:event withType:MiddleButtonDown];
break;
case 4:
case 3:
[self mouseEvent:event withType:XButton1Down];
break;
case 5:
case 4:
[self mouseEvent:event withType:XButton2Down];
break;
@ -487,13 +486,12 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt)
switch(event.buttonNumber)
{
case 2:
case 3:
[self mouseEvent:event withType:MiddleButtonUp];
break;
case 4:
case 3:
[self mouseEvent:event withType:XButton1Up];
break;
case 5:
case 4:
[self mouseEvent:event withType:XButton2Up];
break;
@ -856,9 +854,10 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt)
- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id <NSDraggingInfo>)info
{
auto localPoint = [self convertPoint:[info draggingLocation] toView:self];
auto avnPoint = ToAvnPoint(localPoint);
auto point = [self translateLocalPoint:avnPoint];
NSPoint eventLocation = [info draggingLocation];
auto viewLocation = [self convertPoint:NSMakePoint(0, 0) toView:nil];
auto localPoint = NSMakePoint(eventLocation.x - viewLocation.x, viewLocation.y - eventLocation.y);
auto point = ToAvnPoint(localPoint);
auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]];
NSDragOperation nsop = [info draggingSourceOperationMask];
@ -868,7 +867,7 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt)
return NSDragOperationNone;
int reffects = (int)parent->TopLevelEvents
->DragEvent(type, point, modifiers, effects,
CreateClipboard([info draggingPasteboard], nil),
CreateClipboard([info draggingPasteboard]),
GetAvnDataObjectHandleFromDraggingInfo(info));
NSDragOperation ret = static_cast<NSDragOperation>(0);
@ -943,7 +942,7 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt)
auto window = (AvnWindow*)[self window];
auto peer = [window automationPeer];
if (!peer->IsRootProvider())
if (peer == nullptr || !peer->IsRootProvider())
return nil;
auto clientPoint = [window convertPointFromScreen:point];
@ -980,6 +979,10 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt)
// of the AvnView.
auto window = (AvnWindow*)[self window];
auto peer = [window automationPeer];
if (peer == nullptr)
{
return;
}
auto childPeers = peer->GetChildren();
auto childCount = childPeers != nullptr ? childPeers->GetCount() : 0;

155
native/Avalonia.Native/src/OSX/AvnWindow.mm

@ -35,6 +35,7 @@
bool _canBecomeKeyWindow;
bool _isExtended;
bool _isTransitioningToFullScreen;
bool _isTitlebarSession;
AvnMenu* _menu;
IAvnAutomationPeer* _automationPeer;
AvnAutomationNode* _automationNode;
@ -435,23 +436,28 @@
return;
}
// If the window has been moved into a position where it's "zoomed"
// Then it should be set as Maximized.
if (window->WindowState() != Maximized && window->IsZoomed())
// Don't adjust window state during fullscreen transitions
// as this can interfere with proper decoration restoration
if(!window->IsTransitioningWindowState())
{
window->SetWindowState(Maximized, false);
}
// We should only return the window state to normal if
// the internal window state is maximized, and macOS says
// the window is no longer zoomed (I.E, the user has moved it)
// Stage Manager will "move" the window when repositioning it
// So if the window was "maximized" before, it should stay maximized
else if(window->WindowState() == Maximized && !window->IsZoomed())
{
// If we're moving the window while maximized,
// we need to let macOS handle if it should be resized
// And not handle it ourselves.
window->SetWindowState(Normal, false);
// If the window has been moved into a position where it's "zoomed"
// Then it should be set as Maximized.
if (window->WindowState() != Maximized && window->IsZoomed())
{
window->SetWindowState(Maximized, false);
}
// We should only return the window state to normal if
// the internal window state is maximized, and macOS says
// the window is no longer zoomed (I.E, the user has moved it)
// Stage Manager will "move" the window when repositioning it
// So if the window was "maximized" before, it should stay maximized
else if(window->WindowState() == Maximized && !window->IsZoomed())
{
// If we're moving the window while maximized,
// we need to let macOS handle if it should be resized
// And not handle it ourselves.
window->SetWindowState(Normal, false);
}
}
}
@ -501,68 +507,10 @@
return NO;
}
- (void)forwardToAvnView:(NSEvent *)event
{
auto parent = _parent.tryGetWithCast<WindowImpl>();
if (!parent) {
return;
}
switch(event.type) {
case NSEventTypeLeftMouseDown:
[parent->View mouseDown:event];
break;
case NSEventTypeLeftMouseUp:
[parent->View mouseUp:event];
break;
case NSEventTypeLeftMouseDragged:
[parent->View mouseDragged:event];
break;
case NSEventTypeRightMouseDown:
[parent->View rightMouseDown:event];
break;
case NSEventTypeRightMouseUp:
[parent->View rightMouseUp:event];
break;
case NSEventTypeRightMouseDragged:
[parent->View rightMouseDragged:event];
break;
case NSEventTypeOtherMouseDown:
[parent->View otherMouseDown:event];
break;
case NSEventTypeOtherMouseUp:
[parent->View otherMouseUp:event];
break;
case NSEventTypeOtherMouseDragged:
[parent->View otherMouseDragged:event];
break;
case NSEventTypeMouseMoved:
[parent->View mouseMoved:event];
break;
default:
break;
}
}
- (void)sendEvent:(NSEvent *_Nonnull)event
{
// Event-tracking loop for thick titlebar mouse events
if (event.type == NSEventTypeLeftMouseDown && [self isPointInTitlebar:event.locationInWindow])
{
NSEventMask mask = NSEventMaskLeftMouseDragged | NSEventMaskLeftMouseUp;
NSEvent *ev = event;
while (ev.type != NSEventTypeLeftMouseUp)
{
[self forwardToAvnView:ev];
[super sendEvent:ev];
ev = [NSApp nextEventMatchingMask:mask
untilDate:[NSDate distantFuture]
inMode:NSEventTrackingRunLoopMode
dequeue:YES];
}
[self forwardToAvnView:ev];
[super sendEvent:ev];
return;
if (event.type == NSEventTypeLeftMouseDown) {
_isTitlebarSession = [self isPointInTitlebar:event.locationInWindow];
}
[super sendEvent:event];
@ -603,6 +551,37 @@
}
break;
case NSEventTypeLeftMouseDragged:
case NSEventTypeMouseMoved:
case NSEventTypeLeftMouseUp:
{
// Usually NSToolbar events are passed natively to AvnView when the mouse is inside the control.
// When a drag operation started in NSToolbar leaves the control region, the view does not get any
// events. We will detect this scenario and pass events ourselves.
if(!_isTitlebarSession || [self isPointInTitlebar:event.locationInWindow])
break;
AvnView* view = parent->View;
if(!view)
break;
if(event.type == NSEventTypeLeftMouseDragged)
{
[view mouseDragged:event];
}
else if(event.type == NSEventTypeMouseMoved)
{
[view mouseMoved:event];
}
else if(event.type == NSEventTypeLeftMouseUp)
{
[view mouseUp:event];
}
}
break;
case NSEventTypeMouseEntered:
{
parent->UpdateCursor();
@ -618,6 +597,10 @@
default:
break;
}
if(event.type == NSEventTypeLeftMouseUp) {
_isTitlebarSession = NO;
}
}
}
@ -627,21 +610,30 @@
- (id _Nullable) accessibilityFocusedUIElement
{
if (![self automationPeer]->IsRootProvider())
auto automationPeer = [self automationPeer];
if (automationPeer == nullptr || !automationPeer->IsRootProvider())
return nil;
auto focusedPeer = [self automationPeer]->RootProvider_GetFocus();
auto focusedPeer = automationPeer->RootProvider_GetFocus();
if (focusedPeer == nullptr)
return nil;
return [AvnAccessibilityElement acquire:focusedPeer];
}
- (NSString * _Nullable) accessibilityIdentifier
{
return GetNSStringAndRelease([self automationPeer]->GetAutomationId());
auto automationPeer = [self automationPeer];
if (automationPeer == nullptr)
return nil;
return GetNSStringAndRelease(automationPeer->GetAutomationId());
}
- (IAvnAutomationPeer* _Nonnull) automationPeer
{
auto parent = _parent.tryGet();
if (_automationPeer == nullptr)
if (parent && _automationPeer == nullptr)
{
_automationPeer = parent->BaseEvents->GetAutomationPeer();
_automationNode = new AvnAutomationNode(self);
@ -654,7 +646,8 @@
- (void)raiseChildrenChanged
{
auto parent = _parent.tryGet();
[parent->View raiseAccessibilityChildrenChanged];
if(parent)
[parent->View raiseAccessibilityChildrenChanged];
}
- (void)raiseFocusChanged

2
native/Avalonia.Native/src/OSX/KeyTransform.mm

@ -33,7 +33,7 @@ const KeyInfo keyInfos[] =
{ 0x1A, AvnPhysicalKeyDigit7, AvnKeyD7, '7' },
{ 0x1C, AvnPhysicalKeyDigit8, AvnKeyD8, '8' },
{ 0x19, AvnPhysicalKeyDigit9, AvnKeyD9, '9' },
{ 0x18, AvnPhysicalKeyEqual, AvnKeyOemMinus, '-' },
{ 0x18, AvnPhysicalKeyEqual, AvnKeyOemPlus, '=' },
{ 0x0A, AvnPhysicalKeyIntlBackslash, AvnKeyOem102, 0 },
{ 0x5E, AvnPhysicalKeyIntlRo, AvnKeyOem102, 0 },
{ 0x5D, AvnPhysicalKeyIntlYen, AvnKeyOem5, 0 },

43
native/Avalonia.Native/src/OSX/StorageProvider.mm

@ -320,12 +320,22 @@ public:
}
auto handler = ^(NSModalResponse result) {
int selectedIndex = -1;
if (panel.accessoryView != nil)
{
auto popup = [panel.accessoryView viewWithTag:kFileTypePopupTag];
if ([popup isKindOfClass:[NSPopUpButton class]])
{
selectedIndex = (int)[(NSPopUpButton*)popup indexOfSelectedItem];
}
}
if(result == NSFileHandlingPanelOKButton)
{
auto url = [panel URL];
auto urls = [NSArray<NSURL*> arrayWithObject:url];
auto uriStrings = CreateAvnStringArray(urls);
events->OnCompleted(uriStrings);
events->OnCompletedWithFilter(uriStrings, selectedIndex);
[panel orderOut:panel];
@ -338,7 +348,7 @@ public:
return;
}
events->OnCompleted(nullptr);
events->OnCompletedWithFilter(nullptr, selectedIndex);
};
@ -355,6 +365,35 @@ public:
}
}
virtual HRESULT TryResolveFileReferenceUri(IAvnString* fileUriStr, IAvnString** ret) override {
if (ret == nullptr)
return E_POINTER;
if (fileUriStr == nullptr)
{
*ret = nullptr;
return S_OK;
}
auto fileUri = [NSURL URLWithString:GetNSStringAndRelease(fileUriStr)];
if (fileUri == nil)
{
*ret = nullptr;
return S_OK;
}
auto filePathUri = [fileUri filePathURL];
if (fileUri == nil)
{
*ret = nullptr;
return S_OK;
}
*ret = CreateAvnString([filePathUri absoluteString]);
return S_OK;
}
private:
NSView* CreateAccessoryView() {
// The label. Add attributes per-OS to match the labels that macOS uses.

8
native/Avalonia.Native/src/OSX/TopLevelImpl.h

@ -60,6 +60,14 @@ public:
virtual HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode) override;
virtual HRESULT GetCurrentDisplayId (CGDirectDisplayID* ret) override;
virtual HRESULT BeginDragAndDropOperation(
AvnDragDropEffects effects,
AvnPoint point,
IAvnClipboardDataSource* source,
IAvnDndResultCallback* callback,
void* sourceHandle) override;
protected:
NSCursor *cursor;
virtual void UpdateAppearance();

62
native/Avalonia.Native/src/OSX/TopLevelImpl.mm

@ -6,6 +6,8 @@
#include "TopLevelImpl.h"
#include "AvnTextInputMethod.h"
#include "AvnView.h"
#include "common.h"
#include "clipboard.h"
TopLevelImpl::~TopLevelImpl() {
View = nullptr;
@ -271,6 +273,66 @@ void TopLevelImpl::UpdateAppearance() {
}
HRESULT TopLevelImpl::BeginDragAndDropOperation(
AvnDragDropEffects effects,
AvnPoint point,
IAvnClipboardDataSource* source,
IAvnDndResultCallback* callback,
void* sourceHandle)
{
START_COM_CALL;
if (View == NULL)
return E_FAIL;
auto nsevent = [NSApp currentEvent];
auto nseventType = [nsevent type];
// If current event isn't a mouse one (probably due to malfunctioning user app)
// attempt to forge a new one
if (!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited)
|| (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) {
// For TopLevelImpl, we don't have a Window so we use the View's window
auto window = [View window];
if (window != nil) {
NSRect convertRect = [window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)];
auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y);
CGPoint cgpoint = NSPointToCGPoint(nspoint);
auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft);
nsevent = [NSEvent eventWithCGEvent:cgevent];
CFRelease(cgevent);
}
}
auto itemCount = source->GetItemCount();
auto draggingItems = [NSMutableArray<NSDraggingItem*> arrayWithCapacity:itemCount];
auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments];
NSRect dragItemRect = {(float) point.X, (float) point.Y, [dragItemImage size].width, [dragItemImage size].height};
for (auto i = 0; i < itemCount; ++i)
{
auto item = source->GetItem(i);
auto writeableItem = [[WriteableClipboardItem alloc] initWithItem:item source:source];
auto draggingItem = [[NSDraggingItem alloc] initWithPasteboardWriter:writeableItem];
[draggingItem setDraggingFrame:dragItemRect contents:dragItemImage];
[draggingItems addObject:draggingItem];
}
int op = 0;
int ieffects = (int) effects;
if ((ieffects & (int) AvnDragDropEffects::Copy) != 0)
op |= NSDragOperationCopy;
if ((ieffects & (int) AvnDragDropEffects::Link) != 0)
op |= NSDragOperationLink;
if ((ieffects & (int) AvnDragDropEffects::Move) != 0)
op |= NSDragOperationMove;
[View beginDraggingSessionWithItems:draggingItems
event:nsevent
source:CreateDraggingSource((NSDragOperation) op, callback, sourceHandle)];
return S_OK;
}
void TopLevelImpl::SetClientSize(NSSize size){
[View setFrameSize:size];
}

4
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@ -69,10 +69,6 @@ public:
virtual HRESULT SetFrameThemeVariant(AvnPlatformThemeVariant variant) override;
virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
IAvnClipboard *clipboard, IAvnDndResultCallback *cb,
void *sourceHandle) override;
virtual HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode) override;
virtual bool IsModal();

45
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@ -411,51 +411,6 @@ HRESULT WindowBaseImpl::SetFrameThemeVariant(AvnPlatformThemeVariant variant) {
return S_OK;
}
HRESULT WindowBaseImpl::BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard *clipboard, IAvnDndResultCallback *cb, void *sourceHandle) {
START_COM_CALL;
auto item = TryGetPasteboardItem(clipboard);
[item setString:@"" forType:GetAvnCustomDataType()];
if (item == nil)
return E_INVALIDARG;
if (View == NULL)
return E_FAIL;
auto nsevent = [NSApp currentEvent];
auto nseventType = [nsevent type];
// If current event isn't a mouse one (probably due to malfunctioning user app)
// attempt to forge a new one
if (!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited)
|| (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) {
NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)];
auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y);
CGPoint cgpoint = NSPointToCGPoint(nspoint);
auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft);
nsevent = [NSEvent eventWithCGEvent:cgevent];
CFRelease(cgevent);
}
auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:item];
auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments];
NSRect dragItemRect = {(float) point.X, (float) point.Y, [dragItemImage size].width, [dragItemImage size].height};
[dragItem setDraggingFrame:dragItemRect contents:dragItemImage];
int op = 0;
int ieffects = (int) effects;
if ((ieffects & (int) AvnDragDropEffects::Copy) != 0)
op |= NSDragOperationCopy;
if ((ieffects & (int) AvnDragDropEffects::Link) != 0)
op |= NSDragOperationLink;
if ((ieffects & (int) AvnDragDropEffects::Move) != 0)
op |= NSDragOperationMove;
[View resetPressedMouseButtons];
[View beginDraggingSessionWithItems:@[dragItem] event:nsevent
source:CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)];
return S_OK;
}
bool WindowBaseImpl::IsModal() {
return false;
}

4
native/Avalonia.Native/src/OSX/WindowImpl.h

@ -87,7 +87,9 @@ BEGIN_INTERFACE_MAP()
bool CanBecomeKeyWindow ();
bool CanZoom() override { return _isEnabled && _canMaximize; }
bool IsTransitioningWindowState() { return _transitioningWindowState; }
protected:
virtual NSWindowStyleMask CalculateStyleMask() override;
virtual void UpdateAppearance() override;

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

@ -122,7 +122,7 @@
case AutomationSplitButton: return NSAccessibilityPopUpButtonRole;
case AutomationWindow: return NSAccessibilityWindowRole;
case AutomationPane: return NSAccessibilityGroupRole;
case AutomationHeader: return NSAccessibilityGroupRole;
case AutomationHeader: return @"AXHeading";
case AutomationHeaderItem: return NSAccessibilityButtonRole;
case AutomationTable: return NSAccessibilityTableRole;
case AutomationTitleBar: return NSAccessibilityGroupRole;
@ -133,6 +133,38 @@
}
}
- (NSAccessibilitySubrole)accessibilitySubrole
{
auto landmarkType = _peer->GetLandmarkType();
switch (landmarkType) {
case LandmarkBanner: return @"AXLandmarkBanner";
case LandmarkComplementary: return @"AXLandmarkComplementary";
case LandmarkContentInfo: return @"AXLandmarkContentInfo";
case LandmarkRegion: return @"AXLandmarkRegion";
case LandmarkForm: return @"AXLandmarkForm";
case LandmarkMain: return @"AXLandmarkMain";
case LandmarkNavigation: return @"AXLandmarkNavigation";
case LandmarkSearch: return @"AXLandmarkSearch";
default: return NSAccessibilityUnknownSubrole;
}
}
- (NSString *)accessibilityRoleDescription
{
auto landmarkType = _peer->GetLandmarkType();
switch (landmarkType) {
case LandmarkBanner: return @"banner";
case LandmarkComplementary: return @"complementary";
case LandmarkContentInfo: return @"content";
case LandmarkRegion: return @"region";
case LandmarkForm: return @"form";
case LandmarkMain: return @"main";
case LandmarkNavigation: return @"navigation";
case LandmarkSearch: return @"search";
}
return NSAccessibilityRoleDescription([self accessibilityRole], [self accessibilitySubrole]);
}
- (NSString *)accessibilityIdentifier
{
return GetNSStringAndRelease(_peer->GetAutomationId());
@ -176,10 +208,30 @@
{
return GetNSStringAndRelease(_peer->GetName());
}
else if (_peer->GetAutomationControlType() == AutomationHeader)
{
return [NSNumber numberWithInt:_peer->GetHeadingLevel()];
}
return [super accessibilityValue];
}
- (void)setAccessibilityValue:(id)newValue
{
if (_peer->IsValueProvider())
{
if (newValue == nil)
_peer->ValueProvider_SetValue(nil);
else if ([newValue isKindOfClass:[NSString class]])
_peer->ValueProvider_SetValue([(NSString*)newValue UTF8String]);
}
else if (_peer->IsRangeValueProvider())
{
if ([newValue isKindOfClass:[NSNumber class]])
_peer->RangeValueProvider_SetValue([(NSNumber*)newValue doubleValue]);
}
}
- (id)accessibilityMinValue
{
if (_peer->IsRangeValueProvider())

55
native/Avalonia.Native/src/OSX/cgl.mm

@ -107,6 +107,61 @@ public:
return Context;
}
int texImageIOSurface2D(int target, int internal_format,
int width, int height, int format, int type, void* ioSurface, int plane) override
{
return CGLTexImageIOSurface2D(Context, target, internal_format, width, height, format, type, (IOSurfaceRef)ioSurface, plane);
}
bool GetIOKitRegistryId(uint64_t *value) override {
if (@available(macOS 10.13, *))
{
GLint rendererId;
if(CGLGetParameter(Context, kCGLCPCurrentRendererID, &rendererId) != 0)
return false;
GLint rendererCount = 0;
CGLRendererInfoObj rendererInfo;
if(CGLQueryRendererInfo(0xFFFFFFFF, &rendererInfo, &rendererCount))
return false;
@try
{
for(auto i = 0; i < rendererCount; i++)
{
GLint thisRendererID;
CGLDescribeRenderer(rendererInfo, i, kCGLRPRendererID, &thisRendererID);
if(thisRendererID == rendererId)
{
GLint gpuIDLow = 0;
GLint gpuIDHigh = 0;
if(CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDLow, &gpuIDLow))
return false;
if(CGLDescribeRenderer(rendererInfo, 0, kCGLRPRegistryIDHigh, &gpuIDHigh))
return false;
*value = ((uint64_t)gpuIDHigh << 32) | gpuIDLow;
return true;
}
}
return false;
}
@finally
{
CGLDestroyRendererInfo(rendererInfo);
}
}
else
return false;
}
~AvnGlContext()
{
CGLReleaseContext(Context);

7
native/Avalonia.Native/src/OSX/clipboard.h

@ -0,0 +1,7 @@
#pragma once
#include "common.h"
@interface WriteableClipboardItem : NSObject <NSPasteboardWriting>
- (nonnull instancetype) initWithItem:(nonnull IAvnClipboardDataItem*)item source:(nonnull IAvnClipboardDataSource*)source;
@end

420
native/Avalonia.Native/src/OSX/clipboard.mm

@ -1,206 +1,354 @@
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
#include "common.h"
#include "clipboard.h"
#include "AvnString.h"
class Clipboard : public ComSingleObject<IAvnClipboard, &IID_IAvnClipboard>
{
private:
NSPasteboard* _pb;
NSPasteboardItem* _item;
NSPasteboard* _pasteboard;
public:
FORWARD_IUNKNOWN()
Clipboard(NSPasteboard* pasteboard, NSPasteboardItem* item)
Clipboard(NSPasteboard* pasteboard)
{
if(pasteboard == nil && item == nil)
if (pasteboard == nil)
pasteboard = [NSPasteboard generalPasteboard];
_pb = pasteboard;
_item = item;
_pasteboard = pasteboard;
}
NSPasteboardItem* TryGetItem()
virtual HRESULT GetFormats(int64_t changeCount, IAvnStringArray** ret) override
{
return _item;
START_COM_ARP_CALL;
if (ret == nullptr)
return E_POINTER;
if (changeCount != [_pasteboard changeCount])
return COR_E_OBJECTDISPOSED;
*ret = ConvertPasteboardTypes([_pasteboard types]);
return S_OK;
}
virtual HRESULT GetText (char* type, IAvnString**ppv) override
virtual HRESULT GetItemCount(int64_t changeCount, int* ret) override
{
START_COM_CALL;
START_COM_ARP_CALL;
@autoreleasepool
if (ret == nullptr)
return E_POINTER;
if (changeCount != [_pasteboard changeCount])
return COR_E_OBJECTDISPOSED;
auto items = [_pasteboard pasteboardItems];
*ret = items == nil ? 0 : (int)[items count];
return S_OK;
}
virtual HRESULT GetItemFormats(int index, int64_t changeCount, IAvnStringArray** ret) override
{
START_COM_ARP_CALL;
if (ret == nullptr)
return E_POINTER;
if (changeCount != [_pasteboard changeCount])
return COR_E_OBJECTDISPOSED;
auto item = [[_pasteboard pasteboardItems] objectAtIndex:index];
*ret = ConvertPasteboardTypes([item types]);
return S_OK;
}
static IAvnStringArray* ConvertPasteboardTypes(NSArray<NSPasteboardType> *types)
{
if (types != nil)
{
if(ppv == nullptr)
NSMutableArray<NSString *> *mutableTypes = [types mutableCopy];
// Add png if format list doesn't have PNG,
// but has any other image type that can be converter into PNG
if (![mutableTypes containsObject:NSPasteboardTypePNG])
{
return E_POINTER;
if ([mutableTypes containsObject:NSPasteboardTypeTIFF]
|| [mutableTypes containsObject:@"public.jpeg"])
{
[mutableTypes addObject: NSPasteboardTypePNG];
}
}
NSString* typeString = [NSString stringWithUTF8String:(const char*)type];
NSString* string = _item == nil ? [_pb stringForType:typeString] : [_item stringForType:typeString];
*ppv = CreateAvnString(string);
return S_OK;
return CreateAvnStringArray(mutableTypes);
}
return nil;
}
virtual HRESULT SetStrings(char* type, IAvnStringArray*ppv) override
virtual HRESULT GetItemValueAsString(int index, int64_t changeCount, const char* format, IAvnString** ret) override
{
START_COM_CALL;
START_COM_ARP_CALL;
@autoreleasepool
{
NSArray<NSString*>* data = GetNSArrayOfStringsAndRelease(ppv);
NSString* typeString = [NSString stringWithUTF8String:(const char*)type];
if(_item == nil)
[_pb setPropertyList: data forType: typeString];
else
[_item setPropertyList: data forType:typeString];
return S_OK;
}
if (ret == nullptr)
return E_POINTER;
if (changeCount != [_pasteboard changeCount])
return COR_E_OBJECTDISPOSED;
auto item = [[_pasteboard pasteboardItems] objectAtIndex:index];
auto value = [item stringForType:[NSString stringWithUTF8String:format]];
*ret = value == nil ? nullptr : CreateAvnString(value);
return S_OK;
}
virtual HRESULT GetStrings(char* type, IAvnStringArray**ppv) override
virtual HRESULT GetItemValueAsBytes(int index, int64_t changeCount, const char* format, IAvnString** ret) override
{
START_COM_CALL;
START_COM_ARP_CALL;
@autoreleasepool
if (ret == nullptr)
return E_POINTER;
if (changeCount != [_pasteboard changeCount])
return COR_E_OBJECTDISPOSED;
auto item = [[_pasteboard pasteboardItems] objectAtIndex:index];
auto formatStr = [NSString stringWithUTF8String:format];
auto value = [item dataForType: formatStr];
// If PNG wasn't found, try to convert TIFF or JPEG to PNG
if (value == nil && [formatStr isEqualToString: NSPasteboardTypePNG])
{
*ppv= nil;
NSString* typeString = [NSString stringWithUTF8String:(const char*)type];
NSObject* data = _item == nil ? [_pb propertyListForType: typeString] : [_item propertyListForType: typeString];
if(data == nil)
return S_OK;
if([data isKindOfClass: [NSString class]])
NSData *imageData = nil;
// Try TIFF first
imageData = [item dataForType:NSPasteboardTypeTIFF];
// If no TIFF, try JPEG
if (imageData == nil) {
imageData = [item dataForType:@"public.jpeg"];
}
if (imageData != nil)
{
*ppv = CreateAvnStringArray((NSString*) data);
return S_OK;
auto image = [[NSImage alloc] initWithData:imageData];
NSBitmapImageRep *bitmapRep = nil;
for (NSImageRep *rep in image.representations) {
if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
bitmapRep = (NSBitmapImageRep *)rep;
break;
}
}
if (!bitmapRep) {
[image lockFocus];
bitmapRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, image.size.width, image.size.height)];
[image unlockFocus];
}
value = [bitmapRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
}
NSArray<NSString*>* arr = (NSArray*)data;
for(int c = 0; c < [arr count]; c++)
if(![[arr objectAtIndex:c] isKindOfClass:[NSString class]])
return E_INVALIDARG;
*ppv = CreateAvnStringArray(arr);
return S_OK;
}
*ret = value == nil || [value length] == 0
? nullptr
: CreateByteArray((void*)[value bytes], (int)[value length]);
return S_OK;
}
virtual HRESULT SetText (char* type, char* utf8String) override
virtual HRESULT Clear(int64_t* ret) override
{
START_COM_CALL;
START_COM_ARP_CALL;
@autoreleasepool
{
auto string = [NSString stringWithUTF8String:(const char*)utf8String];
auto typeString = [NSString stringWithUTF8String:(const char*)type];
if(_item == nil)
[_pb setString: string forType: typeString];
else
[_item setString: string forType:typeString];
*ret = [_pasteboard clearContents];
return S_OK;
}
virtual HRESULT GetChangeCount(int64_t* ret) override
{
START_COM_ARP_CALL;
return S_OK;
}
*ret = [_pasteboard changeCount];
return S_OK;
}
virtual HRESULT SetBytes(char* type, void* bytes, int len) override
virtual HRESULT SetData(IAvnClipboardDataSource* source) override
{
START_COM_CALL;
START_COM_ARP_CALL;
@autoreleasepool
auto count = source->GetItemCount();
auto writeableItems = [NSMutableArray<WriteableClipboardItem*> arrayWithCapacity:count];
for (auto i = 0; i < count; ++i)
{
auto typeString = [NSString stringWithUTF8String:(const char*)type];
auto data = [NSData dataWithBytes:bytes length:len];
if(_item == nil)
[_pb setData:data forType:typeString];
else
[_item setData:data forType:typeString];
return S_OK;
auto item = source->GetItem(i);
auto writeableItem = [[WriteableClipboardItem alloc] initWithItem:item source:source];
[writeableItems addObject:writeableItem];
}
[_pasteboard writeObjects:writeableItems];
return S_OK;
}
virtual HRESULT GetBytes(char* type, IAvnString**ppv) override
virtual bool IsTextFormat(const char *format) override
{
START_COM_CALL;
START_COM_ARP_CALL;
auto formatString = [NSString stringWithUTF8String:format];
@autoreleasepool
if (@available(macOS 11.0, *))
{
*ppv = nil;
auto typeString = [NSString stringWithUTF8String:(const char*)type];
NSData*data;
@try
{
if(_item)
data = [_item dataForType:typeString];
else
data = [_pb dataForType:typeString];
if(data == nil)
return E_FAIL;
}
@catch(NSException* e)
{
return E_FAIL;
}
*ppv = CreateByteArray((void*)data.bytes, (int)data.length);
return S_OK;
auto type = [UTType typeWithIdentifier:formatString];
return type != nil && [type conformsToType:UTTypeText];
}
else
{
return UTTypeConformsTo((__bridge CFStringRef)formatString, kUTTypeText);
}
}
};
virtual HRESULT Clear(int64_t* rv) override
extern IAvnClipboard* CreateClipboard(NSPasteboard* pb)
{
return new Clipboard(pb);
}
@implementation WriteableClipboardItem
{
IAvnClipboardDataItem* _item;
IAvnClipboardDataSource* _source;
}
- (nonnull WriteableClipboardItem*) initWithItem:(nonnull IAvnClipboardDataItem*)item source:(nonnull IAvnClipboardDataSource*)source
{
self = [super init];
_item = item;
_source = source;
// Each item references its source so it doesn't get disposed too early.
source->AddRef();
return self;
}
NSString* TryConvertFormatToUti(NSString* format)
{
if (@available(macOS 11.0, *))
{
START_COM_CALL;
@autoreleasepool
auto type = [UTType typeWithIdentifier:format];
if (type == nil)
{
if(_item != nil)
{
_item = [NSPasteboardItem new];
return 0;
}
if ([format containsString:@"/"])
type = [UTType typeWithMIMEType:format];
else
type = [UTType exportedTypeWithIdentifier:format];
if (type == nil)
{
*rv = [_pb clearContents];
[_pb setString:@"" forType:NSPasteboardTypeString];
// For now, we need to use the deprecated UTTypeCreatePreferredIdentifierForTag to create a dynamic UTI for arbitrary strings.
// This is only necessary because the old IDataObject can provide arbitrary types that aren't UTIs nor mime types.
// With the new DataFormat:
// - If the format is an application format, the managed side provides a UTI like net.avaloniaui.app.uti.xxx.
// - If the format is an OS format, the user has been warned that they MUST provide a name which is valid for the OS.
// TODO12: remove!
auto fromPasteboardType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, (__bridge CFStringRef)format, nil);
if (fromPasteboardType != nil)
return (__bridge_transfer NSString*)fromPasteboardType;
}
return S_OK;
}
return type == nil ? nil : [type identifier];
}
virtual HRESULT GetChangeCount(int64_t* rv) override
else
{
START_COM_CALL;
if(_item == nil)
{
*rv = [_pb changeCount];
return S_OK;
}
return E_NOTIMPL;
auto bridgedFormat = (__bridge CFStringRef)format;
if (UTTypeIsDeclared(bridgedFormat))
return format;
auto fromMimeType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, bridgedFormat, nil);
if (fromMimeType != nil)
return (__bridge_transfer NSString*)fromMimeType;
auto fromPasteboardType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, bridgedFormat, nil);
if (fromPasteboardType != nil)
return (__bridge_transfer NSString*)fromPasteboardType;
return nil;
}
}
- (nonnull NSArray<NSPasteboardType>*) writableTypesForPasteboard:(nonnull NSPasteboard*)pasteboard
{
auto formats = _item->ProvideFormats();
if (formats == nullptr)
return [NSArray array];
virtual HRESULT ObtainFormats(IAvnStringArray** ppv) override
auto count = formats->GetCount();
if (count == 0)
return [NSArray array];
auto utis = [NSMutableArray arrayWithCapacity:count];
IAvnString* format;
for (auto i = 0; i < count; ++i)
{
START_COM_CALL;
@autoreleasepool
{
*ppv = CreateAvnStringArray(_item == nil ? [_pb types] : [_item types]);
return S_OK;
}
if (formats->Get(i, &format) != S_OK)
continue;
// Only UTIs must be returned from writableTypesForPasteboard or an exception will be thrown
auto formatString = GetNSStringAndRelease(format);
auto uti = TryConvertFormatToUti(formatString);
if (uti != nil)
[utis addObject:uti];
}
};
formats->Release();
[utis addObject:GetAvnCustomDataType()];
return utis;
}
extern IAvnClipboard* CreateClipboard(NSPasteboard* pb, NSPasteboardItem* item)
- (NSPasteboardWritingOptions) writingOptionsForType:(NSPasteboardType)type pasteboard:(NSPasteboard*)pasteboard
{
return new Clipboard(pb, item);
return [type isEqualToString:NSPasteboardTypeString] || [type isEqualToString:GetAvnCustomDataType()]
? 0
: NSPasteboardWritingPromised;
}
extern NSPasteboardItem* TryGetPasteboardItem(IAvnClipboard*cb)
- (nullable id) pasteboardPropertyListForType:(nonnull NSPasteboardType)type
{
auto clipboard = dynamic_cast<Clipboard*>(cb);
if(clipboard == nil)
if ([type isEqualToString:GetAvnCustomDataType()])
return @"";
ComPtr<IAvnClipboardDataValue> value(_item->GetValue([type UTF8String]), true);
if (value.getRaw() == nullptr)
return nil;
return clipboard->TryGetItem();
if (value->IsString())
return GetNSStringAndRelease(value->AsString());
auto length = value->GetByteLength();
auto buffer = malloc(length);
value->CopyBytesTo(buffer);
return [NSData dataWithBytesNoCopy:buffer length:length];
}
- (void) dealloc
{
if (_item != nullptr)
{
_item->Release();
_item = nullptr;
}
if (_source != nullptr)
{
_source->Release();
_source = nullptr;
}
}
@end

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

@ -16,8 +16,7 @@ extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events);
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events);
extern IAvnStorageProvider* CreateStorageProvider();
extern IAvnScreens* CreateScreens(IAvnScreenEvents* cb);
extern IAvnClipboard* CreateClipboard(NSPasteboard*, NSPasteboardItem*);
extern NSPasteboardItem* TryGetPasteboardItem(IAvnClipboard*);
extern IAvnClipboard* CreateClipboard(NSPasteboard* pb);
extern NSObject<NSDraggingSource>* CreateDraggingSource(NSDragOperation op, IAvnDndResultCallback* cb, void* handle);
extern void* GetAvnDataObjectHandleFromDraggingInfo(NSObject<NSDraggingInfo>* info);
extern NSString* GetAvnCustomDataType();
@ -34,6 +33,7 @@ extern IAvnPlatformBehaviorInhibition* CreatePlatformBehaviorInhibition();
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
extern IAvnPlatformSettings* CreatePlatformSettings();
extern IAvnPlatformRenderTimer* CreatePlatformRenderTimer();
extern IAvnNativeObjectsMemoryManagement* CreateMemoryManagementHelper();
extern void SetAppMenu(IAvnMenu *menu);
extern void SetServicesMenu (IAvnMenu* menu);
extern IAvnMenu* GetAppMenu ();
@ -48,6 +48,7 @@ extern AvnPoint ToAvnPoint (NSPoint p);
extern AvnPoint ConvertPointY (AvnPoint p);
extern NSSize ToNSSize (AvnSize s);
extern AvnSize FromNSSize (NSSize s);
extern IAvnMTLSharedEvent* ImportMTLSharedEvent(void* object);
#ifdef DEBUG
#define NSDebugLog(...) NSLog(__VA_ARGS__)
#else

9
native/Avalonia.Native/src/OSX/crapium.h

@ -0,0 +1,9 @@
// The only reason this file exists is Appium which limits our highest Xcode version to 15.2. Please, purge Appium from our codebase
#ifndef crapium_h
#define crapium_h
#import <Foundation/Foundation.h>
@protocol MTLSharedEvent;
API_AVAILABLE(macos(12))
extern BOOL MtlSharedEventWaitUntilSignaledValueHack(id<MTLSharedEvent> ev, uint64_t value, uint64_t milliseconds);
#endif /* crapium_h */

21
native/Avalonia.Native/src/OSX/crapium.mm

@ -0,0 +1,21 @@
// The only reason this file exists is Appium which limits our highest Xcode version to 15.2. Please, purge Appium from our codebase
#import <Foundation/Foundation.h>
#import "crapium.h"
@class MTLSharedEventHandle;
@protocol MTLSharedEvent;
@protocol MTLEvent;
typedef void (^MTLSharedEventNotificationBlock)(id <MTLSharedEvent>, uint64_t value);
API_AVAILABLE(macos(10.14), ios(12.0))
@protocol MTLSharedEvent <MTLEvent>
// Synchronously wait for the signaledValue to be greater than or equal to 'value', with a timeout
// specified in milliseconds. Returns YES if the value was signaled before the timeout, otherwise NO.
- (BOOL)waitUntilSignaledValue:(uint64_t)value timeoutMS:(uint64_t)milliseconds API_AVAILABLE(macos(12.0), ios(15.0));
@end
API_AVAILABLE(macos(12))
extern BOOL MtlSharedEventWaitUntilSignaledValueHack(id<MTLSharedEvent> ev, uint64_t value, uint64_t milliseconds)
{
return [ev waitUntilSignaledValue:value timeoutMS:milliseconds];
}

14
native/Avalonia.Native/src/OSX/dnd.mm

@ -14,9 +14,17 @@ extern AvnDragDropEffects ConvertDragDropEffects(NSDragOperation nsop)
extern NSString* GetAvnCustomDataType()
{
char buffer[256];
sprintf(buffer, "net.avaloniaui.inproc.uti.n%in", getpid());
return [NSString stringWithUTF8String:buffer];
static NSString* result = nil;
if (result == nil)
{
const size_t bufferSize = 256;
char buffer[bufferSize];
snprintf(buffer, bufferSize, "net.avaloniaui.inproc.uti.n%in", getpid());
result = [NSString stringWithUTF8String:buffer];
}
return result;
}
@interface AvnDndSource : NSObject<NSDraggingSource>

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

@ -312,18 +312,7 @@ public:
@autoreleasepool
{
*ppv = ::CreateClipboard (nil, nil);
return S_OK;
}
}
virtual HRESULT CreateDndClipboard(IAvnClipboard** ppv) override
{
START_COM_CALL;
@autoreleasepool
{
*ppv = ::CreateClipboard (nil, [NSPasteboardItem new]);
*ppv = ::CreateClipboard(nil);
return S_OK;
}
}
@ -478,6 +467,22 @@ public:
return S_OK;
}
}
virtual HRESULT ImportMTLSharedEvent(void* event, IAvnMTLSharedEvent** ppv) override
{
START_COM_CALL;
*ppv = ::ImportMTLSharedEvent(event);
return *ppv != nullptr ? S_OK : E_FAIL;
}
HRESULT CreateMemoryManagementHelper(IAvnNativeObjectsMemoryManagement **ppv) override {
START_COM_CALL;
*ppv = ::CreateMemoryManagementHelper();
return S_OK;
}
};
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()

40
native/Avalonia.Native/src/OSX/memhelp.mm

@ -0,0 +1,40 @@
#include "common.h"
class MemHelper : public ComSingleObject<IAvnNativeObjectsMemoryManagement, &IID_IAvnNativeObjectsMemoryManagement>
{
FORWARD_IUNKNOWN()
void RetainNSObject(void *object) override
{
::RetainNSObject(object);
}
void ReleaseNSObject(void *object) override
{
::ReleaseNSObject(object);
}
void RetainCFObject(void *object) override
{
CFRetain(object);
}
void ReleaseCFObject(void *object) override
{
CFRelease(object);
}
uint64_t GetRetainCountForNSObject(void *obj) override {
return ::GetRetainCountForNSObject(obj);
}
int64_t GetRetainCountForCFObject(void *obj) override {
return CFGetRetainCount(obj);
}
};
extern IAvnNativeObjectsMemoryManagement* CreateMemoryManagementHelper()
{
return new MemHelper();
}

4
native/Avalonia.Native/src/OSX/menu.mm

@ -262,10 +262,10 @@ HRESULT AvnAppMenuItem::SetIcon(void *data, size_t length)
NSSize originalSize = [image size];
NSSize size;
size.height = [[NSFont menuFontOfSize:0] pointSize] * 1.333333;
size.height = floor([[NSFont menuFontOfSize:0] pointSize] * 1.333333);
auto scaleFactor = size.height / originalSize.height;
size.width = originalSize.width * scaleFactor;
size.width = floor(originalSize.width * scaleFactor);
[image setSize: size];
[_native setImage:image];

169
native/Avalonia.Native/src/OSX/metal.mm

@ -3,6 +3,74 @@
#import <QuartzCore/QuartzCore.h>
#include "common.h"
#include "rendertarget.h"
#import "crapium.h"
class API_AVAILABLE(macos(12.0)) AvnMTLSharedEvent : public ComSingleObject<IAvnMTLSharedEvent, &IID_IAvnMTLSharedEvent>
{
id<MTLSharedEvent> _event;
public:
AvnMTLSharedEvent(id<MTLSharedEvent> ev) : _event(ev)
{
}
FORWARD_IUNKNOWN()
id<MTLSharedEvent> GetEvent()
{
return _event;
}
void *GetNativeHandle() override {
return (__bridge void*)_event;
}
bool Wait(uint64_t value, uint64_t timeoutMS) override {
return MtlSharedEventWaitUntilSignaledValueHack(_event, value, timeoutMS);
}
void SetSignaledValue(uint64_t value) override {
_event.signaledValue = value;
}
uint64_t GetSignaledValue() override {
return _event.signaledValue;
}
};
class AvnMetalTexture : public ComSingleObject<IAvnMetalTexture, &IID_IAvnMetalTexture>
{
id<MTLTexture> _texture;
public:
FORWARD_IUNKNOWN()
AvnMetalTexture(id<MTLTexture> texture) : _texture(texture)
{
}
void *GetNativeHandle() override
{
return (__bridge void*)_texture;
}
int GetWidth() override
{
return (int)_texture.width;
}
int GetHeight() override
{
return (int)_texture.height;
}
int GetSampleCount() override
{
return (int)_texture.sampleCount;
}
};
class AvnMetalDevice : public ComSingleObject<IAvnMetalDevice, &IID_IAvnMetalDevice>
{
@ -18,7 +86,86 @@ public:
void *GetQueue() override {
return (__bridge void*) queue;
}
HRESULT ImportIOSurface(void *handle, AvnPixelFormat pixelFormat, IAvnMetalTexture **ppv) override {
auto surf = (IOSurfaceRef)handle;
auto width = IOSurfaceGetWidth(surf);
auto height = IOSurfaceGetHeight(surf);
auto desc = [MTLTextureDescriptor new];
if(pixelFormat == kAvnRgba8888)
desc.pixelFormat = MTLPixelFormatRGBA8Unorm;
else if(pixelFormat == kAvnBgra8888)
desc.pixelFormat = MTLPixelFormatBGRA8Unorm;
else
return E_INVALIDARG;
desc.textureType = MTLTextureType2D;
desc.width = width;
desc.height = height;
desc.depth = 1;
desc.mipmapLevelCount = 1;
desc.sampleCount = 1;
desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
auto texture = [device newTextureWithDescriptor:desc iosurface:surf plane:0];
if(texture == nullptr)
return E_FAIL;
*ppv = new AvnMetalTexture(texture);
return S_OK;
}
HRESULT ImportSharedEvent(void *mtlSharedEventInstance, IAvnMTLSharedEvent**ppv) override {
if (@available(macOS 12.0, *)) {
auto external = (__bridge id<MTLSharedEvent>)mtlSharedEventInstance;
auto handle = external.newSharedEventHandle;
auto imported = [device newSharedEventWithHandle: handle];
*ppv = new AvnMTLSharedEvent(imported);
return S_OK;
}
else
{
return E_NOTIMPL;
}
}
HRESULT SignalOrWait(IAvnMTLSharedEvent *ev, uint64_t value, bool wait)
{
if (@available(macOS 12.0, *))
{
auto e = dynamic_cast<AvnMTLSharedEvent*>(ev);
if(e == nullptr)
return E_FAIL;;
auto buf = [queue commandBuffer];
if(wait)
[buf encodeWaitForEvent:e->GetEvent() value:value];
else
[buf encodeSignalEvent:e->GetEvent() value:value];
[buf commit];
return S_OK;
}
else
return E_FAIL;
}
HRESULT SubmitWait(IAvnMTLSharedEvent *ev, uint64_t value) override {
return SignalOrWait(ev, value, true);
}
HRESULT SubmitSignal(IAvnMTLSharedEvent *ev, uint64_t value) override {
return SignalOrWait(ev, value, false);
}
bool GetIOKitRegistryId(uint64_t *value) override {
if (@available(macOS 10.13, *)) {
*value = [device registryID];
return true;
} else {
return false;
}
}
AvnMetalDevice(id <MTLDevice> device, id <MTLCommandQueue> queue) : device(device), queue(queue) {
}
@ -160,3 +307,23 @@ extern IAvnMetalDisplay* GetMetalDisplay()
{
return _display;
}
extern IAvnMTLSharedEvent* ImportMTLSharedEvent(void* object)
{
if (@available(macOS 12.0, *)) {
if(object == nullptr)
return nil;
auto evId = (__bridge id<MTLSharedEvent>)object;
if(evId == nil)
return nil;
return new AvnMTLSharedEvent(evId);
}
else
{
return nil;
}
}

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

@ -1,5 +1,5 @@
#include "noarc.h"
#include "avalonia-native.h"
CppAutoreleasePool::CppAutoreleasePool()
{
_pool = [[NSAutoreleasePool alloc] init];
@ -9,3 +9,17 @@ CppAutoreleasePool::~CppAutoreleasePool() {
auto ptr = (NSAutoreleasePool*)_pool;
[ptr release];
}
extern void ReleaseNSObject(void* obj)
{
[(NSObject*)obj release];
}
extern void RetainNSObject(void* obj)
{
[(NSObject*)obj retain];
}
extern uint64_t GetRetainCountForNSObject(void* obj)
{
return [(NSObject*)obj retainCount];
}

6
native/Avalonia.Native/src/OSX/trayicon.mm

@ -39,10 +39,10 @@ HRESULT AvnTrayIcon::SetIcon (void* data, size_t length)
NSSize originalSize = [image size];
NSSize size;
size.height = [[NSFont menuFontOfSize:0] pointSize] * 1.333333;
size.height = floor([[NSFont menuFontOfSize:0] pointSize] * 1.333333);
auto scaleFactor = size.height / originalSize.height;
size.width = originalSize.width * scaleFactor;
size.width = floor(originalSize.width * scaleFactor);
[image setSize: size];
[image setTemplate: _isTemplateIcon];

639
nukebuild/ApiDiffHelper.cs

@ -1,313 +1,488 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Collections.Immutable;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
using Nuke.Common.IO;
using Nuke.Common.Tooling;
using Serilog;
using static Serilog.Log;
public static class ApiDiffHelper
{
static readonly HttpClient s_httpClient = new();
const string NightlyFeedUri = "https://nuget-feed-nightly.avaloniaui.net/v3/index.json";
const string MainPackageName = "Avalonia";
const string FolderLib = "lib";
private static readonly Regex s_suppressionPathRegex =
new("<(Left|Right)>(.*?)</(Left|Right)>", RegexOptions.Compiled);
public static void ValidatePackage(
Tool apiCompatTool,
PackageDiffInfo packageDiff,
AbsolutePath rootAssembliesFolderPath,
AbsolutePath suppressionFilesFolderPath,
bool updateSuppressionFile)
{
Information("Validating API for package {Id}", packageDiff.PackageId);
Directory.CreateDirectory(suppressionFilesFolderPath);
var suppressionFilePath = suppressionFilesFolderPath / (packageDiff.PackageId + ".nupkg.xml");
var replaceDirectorySeparators = Path.DirectorySeparatorChar == '\\';
var allErrors = new List<string>();
foreach (var framework in packageDiff.Frameworks)
{
var relativeBaselinePath = rootAssembliesFolderPath.GetRelativePathTo(framework.BaselineFolderPath);
var relativeCurrentPath = rootAssembliesFolderPath.GetRelativePathTo(framework.CurrentFolderPath);
var args = "";
if (suppressionFilePath.FileExists())
{
args += $""" --suppression-file="{suppressionFilePath}" --permit-unnecessary-suppressions """;
if (replaceDirectorySeparators)
ReplaceDirectorySeparators(suppressionFilePath, '/', '\\');
}
if (updateSuppressionFile)
args += $""" --suppression-output-file="{suppressionFilePath}" --generate-suppression-file --preserve-unnecessary-suppressions """;
args += $""" -l="{relativeBaselinePath}" -r="{relativeCurrentPath}" """;
var localErrors = GetErrors(apiCompatTool($"{args:nq}", rootAssembliesFolderPath, exitHandler: _ => { }));
if (replaceDirectorySeparators)
ReplaceDirectorySeparators(suppressionFilePath, '\\', '/');
allErrors.AddRange(localErrors);
}
ThrowOnErrors(allErrors, packageDiff.PackageId, "ValidateApiDiff");
}
public static async Task GetDiff(
Tool apiDiffTool, string outputFolder,
string packagePath, string baselineVersion)
/// <summary>
/// The ApiCompat tool treats paths with '/' and '\' separators as different files.
/// Before running the tool, adjust the existing separators (using a dirty regex) to match the current platform.
/// After running the tool, change all separators back to '/'.
/// </summary>
static void ReplaceDirectorySeparators(AbsolutePath suppressionFilePath, char oldSeparator, char newSeparator)
{
await using var baselineStream = await DownloadBaselinePackage(packagePath, baselineVersion);
if (baselineStream == null)
if (!File.Exists(suppressionFilePath))
return;
if (!Directory.Exists(outputFolder))
var lines = File.ReadAllLines(suppressionFilePath);
for (var i = 0; i < lines.Length; i++)
{
Directory.CreateDirectory(outputFolder!);
var original = lines[i];
var replacement = s_suppressionPathRegex.Replace(original, match =>
{
var path = match.Groups[2].Value.Replace(oldSeparator, newSeparator);
return $"<{match.Groups[1].Value}>{path}</{match.Groups[3].Value}>";
});
lines[i] = replacement;
}
using (var target = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.Read), ZipArchiveMode.Read))
using (var baseline = new ZipArchive(baselineStream, ZipArchiveMode.Read))
using (Helpers.UseTempDir(out var tempFolder))
{
var targetDlls = GetDlls(target);
var baselineDlls = GetDlls(baseline);
File.WriteAllLines(suppressionFilePath, lines);
}
var pairs = new List<(string baseline, string target)>();
public static void GenerateMarkdownDiff(
Tool apiDiffTool,
PackageDiffInfo packageDiff,
AbsolutePath rootOutputFolderPath,
string baselineDisplay,
string currentDisplay)
{
Information("Creating markdown diff for package {Id}", packageDiff.PackageId);
var packageId = GetPackageId(packagePath);
var packageOutputFolderPath = rootOutputFolderPath / packageDiff.PackageId;
Directory.CreateDirectory(packageOutputFolderPath);
// Don't use Path.Combine with these left and right tool parameters.
// Microsoft.DotNet.ApiCompat.Tool is stupid and treats '/' and '\' as different assemblies in suppression files.
// So, always use Unix '/'
foreach (var baselineDll in baselineDlls)
{
var baselineDllPath = await ExtractDll("baseline", baselineDll, tempFolder);
// Not specifying -eattrs incorrectly tries to load AttributesToExclude.txt, create an empty file instead.
// See https://github.com/dotnet/sdk/issues/49719
var excludedAttributesFilePath = (AbsolutePath)Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString());
File.WriteAllBytes(excludedAttributesFilePath!, []);
var targetTfm = baselineDll.target;
var targetDll = targetDlls.FirstOrDefault(e =>
e.target.StartsWith(targetTfm) && e.entry.Name == baselineDll.entry.Name);
if (targetDll is null)
try
{
var allErrors = new List<string>();
// The API diff tool is unbelievably slow, process in parallel.
Parallel.ForEach(
packageDiff.Frameworks,
framework =>
{
if (s_tfmRedirects.FirstOrDefault(t => baselineDll.target.StartsWith(t.oldTfm) && (t.package is null || packageId == t.package)).newTfm is {} newTfm)
var frameworkOutputFolderPath = packageOutputFolderPath / framework.Framework.GetShortFolderName();
var args = $""" -b="{framework.BaselineFolderPath}" -bfn="{baselineDisplay}" -a="{framework.CurrentFolderPath}" -afn="{currentDisplay}" -o="{frameworkOutputFolderPath}" -eattrs="{excludedAttributesFilePath}" """;
var localErrors = GetErrors(apiDiffTool($"{args:nq}"));
if (localErrors.Length > 0)
{
targetTfm = newTfm;
targetDll = targetDlls.FirstOrDefault(e =>
e.target.StartsWith(targetTfm) && e.entry.Name == baselineDll.entry.Name);
lock (allErrors)
allErrors.AddRange(localErrors);
}
}
});
if (targetDll?.entry is null)
{
throw new InvalidOperationException($"Some assemblies are missing in the new package {packageId}: {baselineDll.entry.Name} for {baselineDll.target}");
}
ThrowOnErrors(allErrors, packageDiff.PackageId, "OutputApiDiff");
var targetDllPath = await ExtractDll("target", targetDll, tempFolder);
MergeFrameworkMarkdownDiffFiles(
rootOutputFolderPath,
packageOutputFolderPath,
[..packageDiff.Frameworks.Select(info => info.Framework)]);
pairs.Add((baselineDllPath, targetDllPath));
}
Directory.Delete(packageOutputFolderPath, true);
}
finally
{
File.Delete(excludedAttributesFilePath);
}
}
await Task.WhenAll(pairs.Select(p => Task.Run(() =>
static void MergeFrameworkMarkdownDiffFiles(
AbsolutePath rootOutputFolderPath,
AbsolutePath packageOutputFolderPath,
ImmutableArray<NuGetFramework> frameworks)
{
// At this point, the hierarchy looks like:
// markdown/
// ├─ net8.0/
// │ ├─ api_diff_Avalonia.md
// │ ├─ api_diff_Avalonia.Controls.md
// ├─ netstandard2.0/
// │ ├─ api_diff_Avalonia.md
// │ ├─ api_diff_Avalonia.Controls.md
//
// We want one file per assembly: merge all files with the same name.
// However, it's very likely that the diff is the same for several frameworks: in this case, keep only one file.
var assemblyGroups = frameworks
.SelectMany(GetFrameworkDiffFiles, (framework, filePath) => (framework, filePath))
.GroupBy(x => x.filePath.Name)
.OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase);
foreach (var assemblyGroup in assemblyGroups)
{
using var writer = File.CreateText(rootOutputFolderPath / assemblyGroup.Key.Replace("api_diff_", ""));
var addSeparator = false;
foreach (var similarDiffGroup in assemblyGroup.GroupBy(x => HashFile(x.filePath), ByteArrayEqualityComparer.Instance))
{
var baselineApi = p.baseline + Random.Shared.Next() + ".api.cs";
var targetApi = p.target + Random.Shared.Next() + ".api.cs";
var resultDiff = p.target + ".api.diff.cs";
GenerateApiListing(apiDiffTool, p.baseline, baselineApi, tempFolder);
GenerateApiListing(apiDiffTool, p.target, targetApi, tempFolder);
var args = $"""-c core.autocrlf=false diff --no-index --minimal """;
args += """--ignore-matching-lines="^\[assembly: System.Reflection.AssemblyVersionAttribute" """;
args += $""" --output {resultDiff} {baselineApi} {targetApi}""";
using (var gitProcess = new Process())
{
gitProcess.StartInfo = new ProcessStartInfo
{
CreateNoWindow = true,
RedirectStandardError = false,
RedirectStandardOutput = false,
FileName = "git",
Arguments = args,
WorkingDirectory = tempFolder
};
gitProcess.Start();
gitProcess.WaitForExit();
}
if (addSeparator)
writer.WriteLine();
var resultFile = new FileInfo(Path.Combine(tempFolder, resultDiff));
if (resultFile.Length > 0)
{
resultFile.CopyTo(Path.Combine(outputFolder, Path.GetFileName(resultDiff)), true);
}
})));
using var reader = File.OpenText(similarDiffGroup.First().filePath);
var firstLine = reader.ReadLine();
writer.Write(firstLine);
writer.WriteLine(" (" + string.Join(", ", similarDiffGroup.Select(x => x.framework.GetShortFolderName())) + ")");
while (reader.ReadLine() is { } line)
writer.WriteLine(line);
addSeparator = true;
}
}
}
private static readonly (string package, string oldTfm, string newTfm)[] s_tfmRedirects = new[]
{
// We use StartsWith below comparing these tfm, as we ignore platform versions (like, net6.0-ios16.1).
("Avalonia.Android", "net6.0-android", "net8.0-android"),
("Avalonia.iOS", "net6.0-ios", "net8.0-ios"),
// Browser was changed from net7.0 to net8.0-browser.
("Avalonia.Browser", "net7.0", "net8.0-browser"),
("Avalonia.Browser.Blazor", "net7.0", "net8.0-browser"),
// Designer was moved from netcoreapp to netstandard.
("Avalonia", "netcoreapp2.0", "netstandard2.0"),
("Avalonia", "net461", "netstandard2.0")
};
public static async Task ValidatePackage(
Tool apiCompatTool, string packagePath, string baselineVersion,
string suppressionFilesFolder, bool updateSuppressionFile)
{
if (!Directory.Exists(suppressionFilesFolder))
AbsolutePath[] GetFrameworkDiffFiles(NuGetFramework framework)
{
Directory.CreateDirectory(suppressionFilesFolder!);
var frameworkFolderPath = packageOutputFolderPath / framework.GetShortFolderName();
if (!frameworkFolderPath.DirectoryExists())
return [];
return Directory.GetFiles(frameworkFolderPath, "*.md")
.Where(filePath => Path.GetFileName(filePath) != "api_diff.md")
.Select(filePath => (AbsolutePath)filePath)
.ToArray();
}
await using var baselineStream = await DownloadBaselinePackage(packagePath, baselineVersion);
if (baselineStream == null)
return;
static byte[] HashFile(AbsolutePath filePath)
{
using var stream = File.OpenRead(filePath);
return SHA256.HashData(stream);
}
}
public static void MergePackageMarkdownDiffFiles(
AbsolutePath rootOutputFolderPath,
string baselineDisplay,
string currentDisplay)
{
const string mergedFileName = "_diff.md";
using (var target = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.Read), ZipArchiveMode.Read))
using (var baseline = new ZipArchive(baselineStream, ZipArchiveMode.Read))
using (Helpers.UseTempDir(out var tempFolder))
var filePaths = Directory.EnumerateFiles(rootOutputFolderPath, "*.md")
.Where(filePath => Path.GetFileName(filePath) != mergedFileName)
.Order(StringComparer.OrdinalIgnoreCase)
.ToArray();
using var writer = File.CreateText(rootOutputFolderPath / mergedFileName);
writer.WriteLine($"# API diff between {baselineDisplay} and {currentDisplay}");
if (filePaths.Length == 0)
{
var targetDlls = GetDlls(target);
var baselineDlls = GetDlls(baseline);
writer.WriteLine();
writer.WriteLine("No changes.");
return;
}
var left = new List<string>();
var right = new List<string>();
foreach (var filePath in filePaths)
{
writer.WriteLine();
var packageId = GetPackageId(packagePath);
var suppressionFile = Path.Combine(suppressionFilesFolder, packageId + ".nupkg.xml");
using var reader = File.OpenText(filePath);
// Don't use Path.Combine with these left and right tool parameters.
// Microsoft.DotNet.ApiCompat.Tool is stupid and treats '/' and '\' as different assemblies in suppression files.
// So, always use Unix '/'
foreach (var baselineDll in baselineDlls)
while (reader.ReadLine() is { } line)
{
var baselineDllPath = await ExtractDll("baseline", baselineDll, tempFolder);
if (line.StartsWith('#'))
writer.Write('#');
var targetTfm = baselineDll.target;
var targetDll = targetDlls.FirstOrDefault(e =>
e.target.StartsWith(targetTfm) && e.entry.Name == baselineDll.entry.Name);
if (targetDll?.entry is null)
{
if (s_tfmRedirects.FirstOrDefault(t => baselineDll.target.StartsWith(t.oldTfm) && (t.package is null || packageId == t.package)).newTfm is {} newTfm)
{
targetTfm = newTfm;
targetDll = targetDlls.FirstOrDefault(e =>
e.target.StartsWith(targetTfm) && e.entry.Name == baselineDll.entry.Name);
}
}
if (targetDll?.entry is null && targetDlls.Count == 1)
{
targetDll = targetDlls.First();
Warning(
$"Some assemblies are missing in the new package {packageId}: {baselineDll.entry.Name} for {baselineDll.target}." +
$"Resolved: {targetDll.target} ({targetDll.entry.Name})");
}
writer.WriteLine(line);
}
}
}
if (targetDll?.entry is null)
{
if (packageId == "Avalonia"
&& baselineDll.target is "net461" or "netcoreapp2.0")
{
// In 11.1 we have removed net461 and netcoreapp2.0 targets from Avalonia package.
continue;
}
var actualTargets = string.Join(", ",
targetDlls.Select(d => $"{d.target} ({d.entry.Name})"));
throw new InvalidOperationException(
$"Some assemblies are missing in the new package {packageId}: {baselineDll.entry.Name} for {baselineDll.target}."
+ $"\r\nActual targets: {actualTargets}.");
}
static string[] GetErrors(IEnumerable<Output> outputs)
=> outputs
.Where(output => output.Type == OutputType.Err)
.Select(output => output.Text)
.ToArray();
static void ThrowOnErrors(List<string> errors, string packageId, string taskName)
{
if (errors.Count > 0)
{
throw new AggregateException(
$"{taskName} task has failed for \"{packageId}\" package",
errors.Select(error => new Exception(error)));
}
}
public static async Task<GlobalDiffInfo> DownloadAndExtractPackagesAsync(
IEnumerable<AbsolutePath> currentPackagePaths,
NuGetVersion currentVersion,
bool isReleaseBranch,
AbsolutePath outputFolderPath,
NuGetVersion? forcedBaselineVersion)
{
var downloadContext = await CreateNuGetDownloadContextAsync();
var baselineVersion = forcedBaselineVersion ??
await GetBaselineVersionAsync(downloadContext, currentVersion, isReleaseBranch);
var targetDllPath = await ExtractDll("target", targetDll, tempFolder);
Information("API baseline version is {Baseline} for current version {Current}", baselineVersion, currentVersion);
left.Add(baselineDllPath);
right.Add(targetDllPath);
var memoryStream = new MemoryStream();
var packageDiffs = ImmutableArray.CreateBuilder<PackageDiffInfo>();
foreach (var packagePath in currentPackagePaths)
{
string packageId;
AbsolutePath currentFolderPath;
AbsolutePath baselineFolderPath;
Dictionary<NuGetFramework, string> currentFolderNames;
Dictionary<NuGetFramework, string> baselineFolderNames;
// Extract current package
using (var currentArchive = new ZipArchive(File.OpenRead(packagePath), ZipArchiveMode.Read, leaveOpen: false))
{
using var packageReader = new PackageArchiveReader(currentArchive);
packageId = packageReader.NuspecReader.GetId();
currentFolderPath = outputFolderPath / "current" / packageId;
currentFolderNames = ExtractDiffableAssembliesFromPackage(currentArchive, currentFolderPath);
}
if (left.Any())
// Download baseline package
memoryStream.Position = 0L;
memoryStream.SetLength(0L);
await DownloadBaselinePackageAsync(memoryStream, downloadContext, packageId, baselineVersion);
memoryStream.Position = 0L;
// Extract baseline package
using (var baselineArchive = new ZipArchive(memoryStream, ZipArchiveMode.Read, leaveOpen: true))
{
var args = $""" -l={string.Join(',', left)} -r="{string.Join(',', right)}" """;
if (File.Exists(suppressionFile))
{
args += $""" --suppression-file="{suppressionFile}" """;
}
baselineFolderPath = outputFolderPath / "baseline" / packageId;
baselineFolderNames = ExtractDiffableAssembliesFromPackage(baselineArchive, baselineFolderPath);
}
if (updateSuppressionFile)
{
args += $""" --suppression-output-file="{suppressionFile}" --generate-suppression-file=true """;
}
if (currentFolderNames.Count == 0 && baselineFolderNames.Count == 0)
continue;
var result = apiCompatTool(args, tempFolder)
.Where(t => t.Type == OutputType.Err).ToArray();
if (result.Any())
{
throw new AggregateException(
$"ApiDiffValidation task has failed for \"{Path.GetFileName(packagePath)}\" package",
result.Select(r => new Exception(r.Text)));
}
var frameworkDiffs = new List<FrameworkDiffInfo>();
foreach (var (framework, currentFolderName) in currentFolderNames)
{
// Ignore new frameworks that didn't exist in the baseline package. Empty folders make the ApiDiff tool crash.
if (!baselineFolderNames.TryGetValue(framework, out var baselineFolderName))
continue;
frameworkDiffs.Add(new FrameworkDiffInfo(
framework,
baselineFolderPath / FolderLib / baselineFolderName,
currentFolderPath / FolderLib / currentFolderName));
}
packageDiffs.Add(new PackageDiffInfo(packageId, [..frameworkDiffs]));
}
return new GlobalDiffInfo(baselineVersion, currentVersion, packageDiffs.DrainToImmutable());
}
record DllEntry(string target, ZipArchiveEntry entry);
static IReadOnlyCollection<DllEntry> GetDlls(ZipArchive archive)
static async Task<NuGetDownloadContext> CreateNuGetDownloadContextAsync()
{
return archive.Entries
.Where(e => Path.GetExtension(e.FullName) == ".dll"
// Exclude analyzers and build task, as we don't care about breaking changes there
&& !e.FullName.Contains("analyzers/") && !e.FullName.Contains("analyzers\\")
&& !e.Name.Contains("Avalonia.Build.Tasks"))
.Select(e => (
entry: e,
isRef: e.FullName.Contains("ref/") || e.FullName.Contains("ref\\"),
target: Path.GetDirectoryName(e.FullName)!.Split(new [] { '/', '\\' }).Last())
)
.GroupBy(e => (e.target, e.entry.Name))
.Select(g => g.MaxBy(e => e.isRef))
.Select(e => new DllEntry(e.target, e.entry))
.ToArray();
var packageSource = new PackageSource(NightlyFeedUri) { ProtocolVersion = 3 };
var repository = Repository.Factory.GetCoreV3(packageSource);
var findPackageByIdResource = await repository.GetResourceAsync<FindPackageByIdResource>();
return new NuGetDownloadContext(packageSource, findPackageByIdResource);
}
static async Task<Stream> DownloadBaselinePackage(string packagePath, string baselineVersion)
/// <summary>
/// Finds the baseline version to diff against.
/// On release branches, use the latest stable version.
/// On the main branch and on PRs, use the latest nightly version.
/// This method assumes all packages share the same version.
/// </summary>
static async Task<NuGetVersion> GetBaselineVersionAsync(
NuGetDownloadContext context,
NuGetVersion currentVersion,
bool isReleaseBranch)
{
if (baselineVersion is null)
var versions = await context.FindPackageByIdResource.GetAllVersionsAsync(
MainPackageName,
context.CacheContext,
NullLogger.Instance,
CancellationToken.None);
versions = versions.Where(v => v < currentVersion);
if (isReleaseBranch)
versions = versions.Where(v => !v.IsPrerelease);
return versions.OrderDescending().FirstOrDefault()
?? throw new InvalidOperationException(
$"Could not find a version less than {currentVersion} for package {MainPackageName} in source {context.PackageSource.Source}");
}
static async Task DownloadBaselinePackageAsync(
Stream destinationStream,
NuGetDownloadContext context,
string packageId,
NuGetVersion version)
{
Information("Downloading {Id} {Version} baseline package", packageId, version);
var downloaded = await context.FindPackageByIdResource.CopyNupkgToStreamAsync(
packageId,
version,
destinationStream,
context.CacheContext,
NullLogger.Instance,
CancellationToken.None);
if (!downloaded)
{
throw new InvalidOperationException(
"Build \"api-baseline\" parameter must be set when running Nuke CreatePackages");
$"Could not download version {version} for package {packageId} in source {context.PackageSource.Source}");
}
}
/*
Gets package name from versions like:
Avalonia.0.10.0-preview1
Avalonia.11.0.999-cibuild0037534-beta
Avalonia.11.0.0
*/
var packageId = GetPackageId(packagePath);
Information("Downloading {0} {1} baseline package", packageId, baselineVersion);
static Dictionary<NuGetFramework, string> ExtractDiffableAssembliesFromPackage(
ZipArchive packageArchive,
AbsolutePath destinationFolderPath)
{
var folderByFramework = new Dictionary<NuGetFramework, string>();
try
foreach (var entry in packageArchive.Entries)
{
using var response = await s_httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get,
$"https://www.nuget.org/api/v2/package/{packageId}/{baselineVersion}"), HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync();
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.NotFound)
{
return null;
if (TryGetFrameworkFolderName(entry.FullName) is not { } folderName)
continue;
// Ignore platform versions: assume that e.g. net8.0-android34 and net8.0-android35 are the same for diff purposes.
var framework = WithoutPlatformVersion(NuGetFramework.ParseFolder(folderName));
if (folderByFramework.TryGetValue(framework, out var existingFolderName))
{
if (existingFolderName != folderName)
{
throw new InvalidOperationException(
$"Found two similar frameworks with different platform versions: {existingFolderName} and {folderName}");
}
}
else
folderByFramework.Add(framework, folderName);
var targetFilePath = destinationFolderPath / entry.FullName;
Directory.CreateDirectory(targetFilePath.Parent);
entry.ExtractToFile(targetFilePath, overwrite: true);
}
catch (Exception ex)
return folderByFramework;
static string? TryGetFrameworkFolderName(string entryPath)
{
throw new InvalidOperationException($"Downloading baseline package for {packageId} {baselineVersion} failed.\r" + ex.Message, ex);
if (!entryPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
return null;
var segments = entryPath.Split('/');
if (segments is not [FolderLib, var name, ..])
return null;
return name;
}
// e.g. net8.0-android34.0 to net8.0-android
static NuGetFramework WithoutPlatformVersion(NuGetFramework value)
=> value.HasPlatform && value.PlatformVersion != FrameworkConstants.EmptyVersion ?
new NuGetFramework(value.Framework, value.Version, value.Platform, FrameworkConstants.EmptyVersion) :
value;
}
static async Task<string> ExtractDll(string basePath, DllEntry dllEntry, string targetFolder)
public sealed class GlobalDiffInfo(
NuGetVersion baselineVersion,
NuGetVersion currentVersion,
ImmutableArray<PackageDiffInfo> packages)
{
var dllPath = $"{basePath}/{dllEntry.target}/{dllEntry.entry.Name}";
var dllRealPath = Path.Combine(targetFolder, dllPath);
Directory.CreateDirectory(Path.GetDirectoryName(dllRealPath)!);
await using (var dllFile = File.Create(dllRealPath))
{
await dllEntry.entry.Open().CopyToAsync(dllFile);
}
public NuGetVersion BaselineVersion { get; } = baselineVersion;
public NuGetVersion CurrentVersion { get; } = currentVersion;
public ImmutableArray<PackageDiffInfo> Packages { get; } = packages;
}
return dllPath;
public sealed class PackageDiffInfo(string packageId, ImmutableArray<FrameworkDiffInfo> frameworks)
{
public string PackageId { get; } = packageId;
public ImmutableArray<FrameworkDiffInfo> Frameworks { get; } = frameworks;
}
static void GenerateApiListing(Tool apiDiffTool, string inputFile, string outputFile, string workingDif)
public sealed class FrameworkDiffInfo(
NuGetFramework framework,
AbsolutePath baselineFolderPath,
AbsolutePath currentFolderPath)
{
var args = $""" --assembly={inputFile} --output-path={outputFile} --include-assembly-attributes=true""";
var result = apiDiffTool(args, workingDif)
.Where(t => t.Type == OutputType.Err).ToArray();
if (result.Any())
{
throw new AggregateException($"GetApi tool failed task has failed",
result.Select(r => new Exception(r.Text)));
}
public NuGetFramework Framework { get; } = framework;
public AbsolutePath BaselineFolderPath { get; } = baselineFolderPath;
public AbsolutePath CurrentFolderPath { get; } = currentFolderPath;
}
static string GetPackageId(string packagePath)
sealed class NuGetDownloadContext(PackageSource packageSource, FindPackageByIdResource findPackageByIdResource)
{
return Regex.Replace(
Path.GetFileNameWithoutExtension(packagePath),
"""(\.\d+\.\d+\.\d+(?:-.+)?)$""", "");
public SourceCacheContext CacheContext { get; } = new();
public PackageSource PackageSource { get; } = packageSource;
public FindPackageByIdResource FindPackageByIdResource { get; } = findPackageByIdResource;
}
}

199
nukebuild/Build.cs

@ -9,17 +9,12 @@ using System.Xml.Linq;
using Nuke.Common;
using Nuke.Common.Tooling;
using Nuke.Common.Tools.DotNet;
using Nuke.Common.Tools.Npm;
using static Nuke.Common.EnvironmentInfo;
using static Nuke.Common.IO.FileSystemTasks;
using static Nuke.Common.IO.PathConstruction;
using static Nuke.Common.Tools.MSBuild.MSBuildTasks;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
using static Nuke.Common.Tools.Xunit.XunitTasks;
using static Nuke.Common.Tools.VSWhere.VSWhereTasks;
using static Serilog.Log;
using MicroCom.CodeGenerator;
using NuGet.Configuration;
using NuGet.Versioning;
using Nuke.Common.CI.AzurePipelines;
using Nuke.Common.IO;
@ -35,15 +30,19 @@ partial class Build : NukeBuild
{
BuildParameters Parameters { get; set; }
[PackageExecutable("Microsoft.DotNet.ApiCompat.Tool", "Microsoft.DotNet.ApiCompat.Tool.dll", Framework = "net6.0")]
#nullable enable
ApiDiffHelper.GlobalDiffInfo? GlobalDiff { get; set; }
#nullable restore
[NuGetPackage("Microsoft.DotNet.ApiCompat.Tool", "Microsoft.DotNet.ApiCompat.Tool.dll", Framework = "net8.0")]
Tool ApiCompatTool;
[PackageExecutable("Microsoft.DotNet.GenAPI.Tool", "Microsoft.DotNet.GenAPI.Tool.dll", Framework = "net8.0")]
Tool ApiGenTool;
[PackageExecutable("dotnet-ilrepack", "ILRepackTool.dll", Framework = "net8.0")]
[NuGetPackage("Microsoft.DotNet.ApiDiff.Tool", "Microsoft.DotNet.ApiDiff.Tool.dll", Framework = "net8.0")]
Tool ApiDiffTool;
[NuGetPackage("dotnet-ilrepack", "ILRepackTool.dll", Framework = "net8.0")]
Tool IlRepackTool;
protected override void OnBuildInitialized()
{
Parameters = new BuildParameters(this, ScheduledTargets.Contains(BuildToNuGetCache));
@ -58,6 +57,7 @@ partial class Build : NukeBuild
Information("Repository Name: " + Parameters.RepositoryName);
Information("Repository Branch: " + Parameters.RepositoryBranch);
}
Information("Configuration: " + Parameters.Configuration);
Information("IsLocalBuild: " + Parameters.IsLocalBuild);
Information("IsRunningOnUnix: " + Parameters.IsRunningOnUnix);
@ -74,8 +74,9 @@ partial class Build : NukeBuild
void ExecWait(string preamble, string command, string args)
{
Console.WriteLine(preamble);
Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
Process.Start(new ProcessStartInfo(command, args) { UseShellExecute = false }).WaitForExit();
}
ExecWait("dotnet version:", "dotnet", "--info");
ExecWait("dotnet workloads:", "dotnet", "workload list");
Information("Processor count: " + Environment.ProcessorCount);
@ -91,7 +92,7 @@ partial class Build : NukeBuild
c.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_11_X64"));
c.AddProperty("PackageVersion", Parameters.Version)
.SetConfiguration(Parameters.Configuration)
.SetVerbosity(DotNetVerbosity.Minimal);
.SetVerbosity(DotNetVerbosity.minimal);
if (Parameters.IsPackingToLocalCache)
c
.AddProperty("ForcePackAvaloniaNative", "True")
@ -100,6 +101,7 @@ partial class Build : NukeBuild
.AddProperty("SkipBuildingTests", "True");
return c;
}
DotNetBuildSettings ApplySetting(DotNetBuildSettings c, Configure<DotNetBuildSettings> configurator = null) =>
ApplySettingCore(c).Build.Apply(configurator);
@ -111,27 +113,34 @@ partial class Build : NukeBuild
Target Clean => _ => _.Executes(() =>
{
Parameters.BuildDirs.ForEach(DeleteDirectory);
EnsureCleanDirectory(Parameters.ArtifactsDir);
EnsureCleanDirectory(Parameters.NugetIntermediateRoot);
EnsureCleanDirectory(Parameters.NugetRoot);
EnsureCleanDirectory(Parameters.ZipRoot);
EnsureCleanDirectory(Parameters.TestResultsRoot);
foreach (var buildDir in Parameters.BuildDirs)
{
Information("Deleting {Directory}", buildDir);
buildDir.DeleteDirectory();
}
CleanDirectory(Parameters.ArtifactsDir);
CleanDirectory(Parameters.NugetIntermediateRoot);
CleanDirectory(Parameters.NugetRoot);
CleanDirectory(Parameters.ZipRoot);
CleanDirectory(Parameters.TestResultsRoot);
void CleanDirectory(AbsolutePath path)
{
Information("Cleaning {Path}", path);
path.CreateOrCleanDirectory();
}
});
Target CompileHtmlPreviewer => _ => _
.DependsOn(Clean)
.OnlyWhenStatic(() => !Parameters.SkipPreviewer)
// Ensure that Bun.Official.Tool is downloaded at least once on CI to work around https://github.com/dotnet/sdk/issues/51831
Target InitDnx => _ => _
.Executes(() =>
{
var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp";
NpmTasks.NpmInstall(c => c
.SetProcessWorkingDirectory(webappDir)
.SetProcessArgumentConfigurator(a => a.Add("--silent")));
NpmTasks.NpmRun(c => c
.SetProcessWorkingDirectory(webappDir)
.SetCommand("dist"));
var process = ProcessTasks.StartProcess(
"dnx",
"Bun.Unofficial.Tool --yes -- install",
$"{RootDirectory}/src/Browser/Avalonia.Browser/webapp");
process.AssertZeroExitCode();
});
Target CompileNative => _ => _
@ -146,8 +155,7 @@ partial class Build : NukeBuild
});
Target Compile => _ => _
.DependsOn(Clean, CompileNative)
.DependsOn(CompileHtmlPreviewer)
.DependsOn(Clean, CompileNative, InitDnx)
.Executes(() =>
{
DotNetBuild(c => ApplySetting(c)
@ -171,6 +179,14 @@ partial class Build : NukeBuild
});
void RunCoreTest(string projectName)
{
RunCoreTest(projectName, (project, tfm) =>
{
DotNetTest(c => ApplySetting(c, project,tfm));
});
}
void RunCoreTest(string projectName, Action<string, string> runTest)
{
Information($"Running tests from {projectName}");
var project = RootDirectory.GlobFiles(@$"**\{projectName}.csproj").FirstOrDefault()
@ -199,11 +215,11 @@ partial class Build : NukeBuild
var tfm = fw;
if (tfm == "$(AvsCurrentTargetFramework)")
{
tfm = "net8.0";
tfm = "net10.0";
}
if (tfm == "$(AvsLegacyTargetFrameworks)")
{
tfm = "net6.0";
tfm = "net8.0";
}
if (tfm.StartsWith("net4")
@ -216,30 +232,29 @@ partial class Build : NukeBuild
Information($"Running for {projectName} ({tfm}) ...");
DotNetTest(c => ApplySetting(c)
.SetProjectFile(project)
.SetFramework(tfm)
.EnableNoBuild()
.EnableNoRestore()
.When(Parameters.PublishTestResults, _ => _
.SetLoggers("trx")
.SetResultsDirectory(Parameters.TestResultsRoot)));
runTest(project, tfm);
}
}
DotNetTestSettings ApplySetting(DotNetTestSettings settings, string project, string tfm) =>
ApplySetting(settings)
.SetProjectFile(project)
.SetFramework(tfm)
.EnableNoBuild()
.EnableNoRestore()
.When(_ => Parameters.PublishTestResults, _ => _
.SetLoggers("trx")
.SetResultsDirectory(Parameters.TestResultsRoot));
Target RunHtmlPreviewerTests => _ => _
.DependsOn(CompileHtmlPreviewer)
.OnlyWhenStatic(() => !(Parameters.SkipPreviewer || Parameters.SkipTests))
.OnlyWhenStatic(() => !(Parameters.SkipTests))
.Executes(() =>
{
var webappTestDir = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests" / "Remote" / "HtmlTransport" / "webapp";
NpmTasks.NpmInstall(c => c
.SetProcessWorkingDirectory(webappTestDir)
.SetProcessArgumentConfigurator(a => a.Add("--silent")));
NpmTasks.NpmRun(c => c
.SetProcessWorkingDirectory(webappTestDir)
.SetCommand("test"));
var webappTest = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests";
DotNetMSBuild(o => o
.SetProcessWorkingDirectory(webappTest)
.SetTargets("BunRunTests"));
});
Target RunCoreLibsTests => _ => _
@ -252,9 +267,10 @@ partial class Build : NukeBuild
RunCoreTest("Avalonia.Markup.UnitTests");
RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
RunCoreTest("Avalonia.Skia.UnitTests");
RunCoreTest("Avalonia.ReactiveUI.UnitTests");
RunCoreTest("Avalonia.Headless.NUnit.UnitTests");
RunCoreTest("Avalonia.Headless.XUnit.UnitTests");
RunCoreTest("Avalonia.Headless.NUnit.PerAssembly.UnitTests");
RunCoreTest("Avalonia.Headless.NUnit.PerTest.UnitTests");
RunCoreTest("Avalonia.Headless.XUnit.PerAssembly.UnitTests");
RunCoreTest("Avalonia.Headless.XUnit.PerTest.UnitTests");
});
Target RunRenderTests => _ => _
@ -263,8 +279,6 @@ partial class Build : NukeBuild
.Executes(() =>
{
RunCoreTest("Avalonia.Skia.RenderTests");
if (Parameters.IsRunningOnWindows)
RunCoreTest("Avalonia.Direct2D1.RenderTests");
});
Target RunToolsTests => _ => _
@ -282,11 +296,7 @@ partial class Build : NukeBuild
.DependsOn(Compile)
.Executes(() =>
{
void DoMemoryTest()
{
RunCoreTest("Avalonia.LeakTests");
}
ControlFlow.ExecuteWithRetry(DoMemoryTest, delay: TimeSpan.FromMilliseconds(3));
RunCoreTest("Avalonia.LeakTests");
});
Target ZipFiles => _ => _
@ -313,7 +323,7 @@ partial class Build : NukeBuild
Parameters.Version + ".nupkg",
IlRepackTool);
var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.config");
EnsureCleanDirectory(Parameters.NugetRoot);
Parameters.NugetRoot.CreateOrCleanDirectory();
if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,
new NumergeNukeLogger()))
throw new Exception("Package merge failed");
@ -321,27 +331,56 @@ partial class Build : NukeBuild
Parameters.NugetRoot / $"Avalonia.{Parameters.Version}.nupkg",
Parameters.NugetRoot / $"Avalonia.{Parameters.Version}.snupkg");
});
Target ValidateApiDiff => _ => _
Target DownloadApiBaselinePackages => _ => _
.DependsOn(CreateNugetPackages)
.Executes(async () =>
{
await Task.WhenAll(
Directory.GetFiles(Parameters.NugetRoot, "*.nupkg").Select(nugetPackage => ApiDiffHelper.ValidatePackage(
ApiCompatTool, nugetPackage, Parameters.ApiValidationBaseline,
Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression)));
GlobalDiff = await ApiDiffHelper.DownloadAndExtractPackagesAsync(
Directory.EnumerateFiles(Parameters.NugetRoot, "*.nupkg").Select(path => (AbsolutePath)path),
NuGetVersion.Parse(Parameters.Version),
Parameters.IsReleaseBranch,
Parameters.ArtifactsDir / "api-diff" / "assemblies",
Parameters.ForceApiValidationBaseline is { } forcedBaseline ? NuGetVersion.Parse(forcedBaseline) : null);
});
Target ValidateApiDiff => _ => _
.DependsOn(DownloadApiBaselinePackages)
.Executes(() =>
{
var globalDiff = GlobalDiff!;
Parallel.ForEach(
globalDiff.Packages,
packageDiff => ApiDiffHelper.ValidatePackage(
ApiCompatTool,
packageDiff,
Parameters.ArtifactsDir / "api-diff" / "assemblies",
Parameters.ApiValidationSuppressionFiles,
Parameters.UpdateApiValidationSuppression));
});
Target OutputApiDiff => _ => _
.DependsOn(CreateNugetPackages)
.Executes(async () =>
.DependsOn(DownloadApiBaselinePackages)
.Executes(() =>
{
await Task.WhenAll(
Directory.GetFiles(Parameters.NugetRoot, "*.nupkg").Select(nugetPackage => ApiDiffHelper.GetDiff(
ApiGenTool, RootDirectory / "api" / "diff",
nugetPackage, Parameters.ApiValidationBaseline)));
var globalDiff = GlobalDiff!;
var outputFolderPath = Parameters.ArtifactsDir / "api-diff" / "markdown";
var baselineDisplay = globalDiff.BaselineVersion.ToString();
var currentDisplay = globalDiff.CurrentVersion.ToString();
Parallel.ForEach(
globalDiff.Packages,
packageDiff => ApiDiffHelper.GenerateMarkdownDiff(
ApiDiffTool,
packageDiff,
outputFolderPath,
baselineDisplay,
currentDisplay));
ApiDiffHelper.MergePackageMarkdownDiffFiles(outputFolderPath, baselineDisplay, currentDisplay);
});
Target RunTests => _ => _
.DependsOn(RunCoreLibsTests)
.DependsOn(RunRenderTests)
@ -418,7 +457,7 @@ partial class Build : NukeBuild
var artifactsDirectory = buildTestsDirectory / "artifacts";
var nugetCacheDirectory = artifactsDirectory / "nuget-cache";
DeleteDirectory(artifactsDirectory);
artifactsDirectory.DeleteDirectory();
BuildTestsAndVerify("Debug");
BuildTestsAndVerify("Release");
@ -432,7 +471,7 @@ partial class Build : NukeBuild
.SetProperty("NuGetPackageRoot", nugetCacheDirectory)
.SetPackageDirectory(nugetCacheDirectory)
.SetProjectFile(buildTestsDirectory / "BuildTests.sln")
.SetProcessArgumentConfigurator(arguments => arguments.Add("--nodeReuse:false")));
.SetProcessAdditionalArguments("--nodeReuse:false"));
// Standard compilation - should have compiled XAML
VerifyBuildTestAssembly("bin", "BuildTests");
@ -461,7 +500,7 @@ partial class Build : NukeBuild
.SetPackageDirectory(nugetCacheDirectory)
.SetNoBuild(noBuild)
.SetProject(buildTestsDirectory / projectName / (projectName + ".csproj"))
.SetProcessArgumentConfigurator(arguments => arguments.Add("--nodeReuse:false")));
.SetProcessAdditionalArguments("--nodeReuse:false"));
void VerifyBuildTestAssembly(string folder, string projectName)
=> XamlCompilationVerifier.VerifyAssemblyCompiledXaml(

52
nukebuild/BuildParameters.cs

@ -1,3 +1,5 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
@ -7,40 +9,35 @@ using System.Xml.Linq;
using Nuke.Common;
using Nuke.Common.CI.AzurePipelines;
using Nuke.Common.IO;
using static Nuke.Common.IO.PathConstruction;
public partial class Build
{
[Parameter(Name = "configuration")]
public string Configuration { get; set; }
public string? Configuration { get; set; }
[Parameter(Name = "skip-tests")]
public bool SkipTests { get; set; }
[Parameter(Name = "force-nuget-version")]
public string ForceNugetVersion { get; set; }
[Parameter(Name = "skip-previewer")]
public bool SkipPreviewer { get; set; }
public string? ForceNugetVersion { get; set; }
[Parameter(Name = "api-baseline")]
public string ApiValidationBaseline { get; set; }
[Parameter(Name = "force-api-baseline")]
public string? ForceApiValidationBaseline { get; set; }
[Parameter(Name = "update-api-suppression")]
public bool? UpdateApiValidationSuppression { get; set; }
[Parameter(Name = "version-output-dir")]
public AbsolutePath VersionOutputDir { get; set; }
public AbsolutePath? VersionOutputDir { get; set; }
public class BuildParameters
{
public string Configuration { get; }
public bool SkipTests { get; }
public bool SkipPreviewer {get;}
public string MainRepo { get; }
public string MasterBranch { get; }
public string RepositoryName { get; }
public string RepositoryBranch { get; }
public string? RepositoryName { get; }
public string? RepositoryBranch { get; }
public string ReleaseConfiguration { get; }
public Regex ReleaseBranchRegex { get; }
public string MSBuildSolution { get; }
@ -66,21 +63,20 @@ public partial class Build
public AbsolutePath ZipRoot { get; }
public AbsolutePath TestResultsRoot { get; }
public string DirSuffix { get; }
public List<string> BuildDirs { get; }
public List<AbsolutePath> BuildDirs { get; }
public string FileZipSuffix { get; }
public AbsolutePath ZipCoreArtifacts { get; }
public AbsolutePath ZipNuGetArtifacts { get; }
public string ApiValidationBaseline { get; }
public string? ForceApiValidationBaseline { get; }
public bool UpdateApiValidationSuppression { get; }
public AbsolutePath ApiValidationSuppressionFiles { get; }
public AbsolutePath VersionOutputDir { get; }
public AbsolutePath? VersionOutputDir { get; }
public BuildParameters(Build b, bool isPackingToLocalCache)
{
// ARGUMENTS
Configuration = b.Configuration ?? "Release";
SkipTests = b.SkipTests;
SkipPreviewer = b.SkipPreviewer;
// CONFIGURATION
MainRepo = "https://github.com/AvaloniaUI/Avalonia";
@ -115,10 +111,9 @@ public partial class Build
IsNuGetRelease = IsMainRepo && IsReleasable && IsReleaseBranch;
// VERSION
var (propsVersion, propsApiCompatVersion) = GetVersion();
Version = b.ForceNugetVersion ?? propsVersion;
Version = b.ForceNugetVersion ?? GetVersion();
ApiValidationBaseline = b.ApiValidationBaseline ?? propsApiCompatVersion;
ForceApiValidationBaseline = b.ForceApiValidationBaseline;
UpdateApiValidationSuppression = b.UpdateApiValidationSuppression ?? IsLocalBuild;
if (IsRunningOnAzure)
@ -126,7 +121,9 @@ public partial class Build
if (!IsNuGetRelease)
{
// Use AssemblyVersion with Build as version
Version += "-cibuild" + int.Parse(Environment.GetEnvironmentVariable("BUILD_BUILDID")).ToString("0000000") + "-alpha";
var buildId = Environment.GetEnvironmentVariable("BUILD_BUILDID") ??
throw new InvalidOperationException("Missing environment variable BUILD_BUILDID");
Version += "-cibuild" + int.Parse(buildId).ToString("0000000") + "-alpha";
}
PublishTestResults = true;
@ -144,10 +141,10 @@ public partial class Build
NugetIntermediateRoot = RootDirectory / "build-intermediate" / "nuget";
ZipRoot = ArtifactsDir / "zip";
TestResultsRoot = ArtifactsDir / "test-results";
BuildDirs = GlobDirectories(RootDirectory, "**/bin")
.Concat(GlobDirectories(RootDirectory, "**/obj"))
.Where(dir => !dir.Contains("nukebuild"))
.Concat(GlobDirectories(RootDirectory, "**/node_modules"))
BuildDirs = RootDirectory.GlobDirectories("**/bin")
.Concat(RootDirectory.GlobDirectories("**/obj"))
.Where(dir => !((string)dir).Contains("nukebuild"))
.Concat(RootDirectory.GlobDirectories("**/node_modules"))
.ToList();
DirSuffix = Configuration;
FileZipSuffix = Version + ".zip";
@ -157,13 +154,10 @@ public partial class Build
VersionOutputDir = b.VersionOutputDir;
}
(string Version, string ApiCompatVersion) GetVersion()
string GetVersion()
{
var xdoc = XDocument.Load(RootDirectory / "build/SharedVersion.props");
return (
xdoc.Descendants().First(x => x.Name.LocalName == "Version").Value,
xdoc.Descendants().First(x => x.Name.LocalName == "ApiCompatVersion").Value
);
return xdoc.Descendants().First(x => x.Name.LocalName == "Version").Value;
}
}

2
nukebuild/BuildTasksPatcher.cs

@ -85,7 +85,7 @@ public class BuildTasksPatcher
var cecilMdbAsm = GetAssemblyPath(typeof(Mono.Cecil.Mdb.MdbReaderProvider));
ilRepackTool.Invoke(
$"/internalize /out:\"{output}\" \"{temp}\" \"{cecilAsm}\" \"{cecilRocksAsm}\" \"{cecilPdbAsm}\" \"{cecilMdbAsm}\"",
$"/internalize /out:\"{output:nq}\" \"{temp:nq}\" \"{cecilAsm:nq}\" \"{cecilRocksAsm:nq}\" \"{cecilPdbAsm:nq}\" \"{cecilMdbAsm:nq}\"",
tempDir);
// 'hurr-durr assembly with the same name is already loaded' prevention

25
nukebuild/ByteArrayEqualityComparer.cs

@ -0,0 +1,25 @@
#nullable enable
using System;
using System.Collections.Generic;
public sealed class ByteArrayEqualityComparer : IEqualityComparer<byte[]>
{
public static ByteArrayEqualityComparer Instance { get; } = new();
public bool Equals(byte[]? x, byte[]? y) {
if (ReferenceEquals(x, y))
return true;
if (x is null || y is null)
return false;
return x.AsSpan().SequenceEqual(y.AsSpan());
}
public int GetHashCode(byte[]? obj)
{
var hashCode = new HashCode();
hashCode.AddBytes(obj.AsSpan());
return hashCode.ToHashCode();
}
}

20
nukebuild/_build.csproj

@ -9,25 +9,17 @@
<TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
<!-- See https://github.com/nuke-build/nuke/issues/818 -->
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
<!-- Necessary for Microsoft.DotNet.GenAPI.Tool -->
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8-transport/nuget/v3/index.json</RestoreAdditionalProjectSources>
</PropertyGroup>
<Import Project="..\build\JetBrains.dotMemoryUnit.props" />
<ItemGroup>
<PackageReference Include="Nuke.Common" Version="6.2.1" />
<PackageReference Include="vswhere" Version="2.6.7" Condition=" '$(OS)' == 'Windows_NT' " />
<PackageReference Include="Nuke.Common" Version="9.0.4" />
<PackageReference Include="MicroCom.CodeGenerator" Version="0.11.0" />
<!-- Keep in sync with Avalonia.Build.Tasks -->
<PackageReference Include="Mono.Cecil" Version="0.11.5" />
<PackageReference Include="Microsoft.Build.Framework" Version="17.3.2" PrivateAssets="All" />
<PackageReference Include="xunit.runner.console" Version="2.4.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageDownload Include="Microsoft.DotNet.ApiCompat.Tool" Version="[8.0.200]" />
<PackageDownload Include="Microsoft.DotNet.GenAPI.Tool" Version="[8.0.300-preview.24115.44]" />
<PackageReference Include="Mono.Cecil" Version="0.11.6" />
<PackageReference Include="Microsoft.Build.Framework" Version="18.0.2" PrivateAssets="All" />
<PackageReference Include="NuGet.Protocol" Version="7.0.1" />
<PackageDownload Include="Microsoft.DotNet.ApiCompat.Tool" Version="[10.0.100]" />
<PackageDownload Include="Microsoft.DotNet.ApiDiff.Tool" Version="[10.0.100-rtm.25531.102]" />
<PackageDownload Include="dotnet-ilrepack" Version="[1.0.0]" />
</ItemGroup>

12
packages/Avalonia/Avalonia.csproj

@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
<TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks)</TargetFrameworks>
<PackageId>Avalonia</PackageId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia.BuildServices" Version="0.0.31" />
<PackageReference Include="Avalonia.BuildServices" Version="11.3.2" />
<ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" />
<ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj"
PrivateAssets="all" />
@ -27,13 +27,13 @@
<MSBuild Projects="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj" Properties="Configuration=$(Configuration);&#xA;Platform=$(Platform)" />
<ItemGroup>
<_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/netstandard2.0/Avalonia.Designer.HostApp.dll">
<PackagePath>tools/netstandard2.0/designer</PackagePath>
<_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/$(AvsCurrentTargetFramework)/Avalonia.Designer.HostApp.dll">
<PackagePath>tools/$(AvsCurrentTargetFramework)/designer</PackagePath>
<Visible>false</Visible>
<BuildAction>None</BuildAction>
</_PackageFiles>
<_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/net461/Avalonia.Designer.HostApp.exe">
<PackagePath>tools/net461/designer</PackagePath>
<_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/$(AvsLegacyTargetFrameworks)/Avalonia.Designer.HostApp.dll">
<PackagePath>tools/$(AvsLegacyTargetFrameworks)/designer</PackagePath>
<Visible>false</Visible>
<BuildAction>None</BuildAction>
</_PackageFiles>

5
packages/Avalonia/Avalonia.props

@ -1,12 +1,11 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaPreviewerNetFullToolPath>$(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe</AvaloniaPreviewerNetFullToolPath>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\..\tools\$(AvsCurrentTargetFramework)\designer\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<UsedAvaloniaProducts>$(UsedAvaloniaProducts);AvaloniaUI</UsedAvaloniaProducts>
<EnableAvaloniaXamlCompilation Condition="'$(EnableAvaloniaXamlCompilation)'==''">true</EnableAvaloniaXamlCompilation>
<AvaloniaXamlIlVerifyIl Condition="'$(AvaloniaXamlIlVerifyIl)'==''">false</AvaloniaXamlIlVerifyIl>
<AvaloniaUseCompiledBindingsByDefault Condition="'$(AvaloniaUseCompiledBindingsByDefault)'==''">false</AvaloniaUseCompiledBindingsByDefault>
<AvaloniaUseCompiledBindingsByDefault Condition="'$(AvaloniaUseCompiledBindingsByDefault)'==''">true</AvaloniaUseCompiledBindingsByDefault>
<AvaloniaNameGeneratorAttachDevTools Condition="'$(AvaloniaNameGeneratorAttachDevTools)' == ''">true</AvaloniaNameGeneratorAttachDevTools>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/>

2
packages/Avalonia/AvaloniaPrivateApis.targets

@ -13,7 +13,7 @@
</Target>
<Target Name="AddReferencePathsToRealAvaloniaAssemblies" BeforeTargets="CoreCompile" Condition="'$(AvaloniaAccessUnstablePrivateApis.ToLowerInvariant())'=='true'">
<PropertyGroup>
<AvaloniaUnstableApiFrameworkToUse>net6.0</AvaloniaUnstableApiFrameworkToUse>
<AvaloniaUnstableApiFrameworkToUse>net8.0</AvaloniaUnstableApiFrameworkToUse>
<AvaloniaUnstableApiFrameworkToUse Condition="$(TargetFramework.StartsWith('net4')) == 'true' or $(TargetFramework.StartsWith('net5')) == 'true' or $(TargetFramework.StartsWith('netsta')) == 'true' or $(TargetFramework.StartsWith('netcore')) == 'true'">netstandard2.0</AvaloniaUnstableApiFrameworkToUse>
</PropertyGroup>
<ItemGroup>

4
packages/Avalonia/AvaloniaSingleProject.targets

@ -98,7 +98,7 @@
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tvos'">13.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'macos'">10.15</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>
@ -278,7 +278,7 @@
Include="$(TizenSharedPrefix)\**\*"
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);@(TizenTpkUserExcludeFiles)" />
</ItemGroup>
<!-- Android workaround to allow projects without AndroidManifest.xml, as it used to be supported -->
<Target Name="BeforeGetAndroidPackageName"
BeforeTargets="_GetAndroidPackageName">

2
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -26,7 +26,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.0.1.4" />
<PackageReference Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.0.1.17" />
</ItemGroup>
<ItemGroup>

10
samples/ControlCatalog.Browser.Blazor/App.razor

@ -1,10 +0,0 @@
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>

10
samples/ControlCatalog.Browser.Blazor/App.razor.cs

@ -1,10 +0,0 @@
using System;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Browser.Blazor;
namespace ControlCatalog.Browser.Blazor;
public partial class App
{
}

29
samples/ControlCatalog.Browser.Blazor/ControlCatalog.Browser.Blazor.csproj

@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>$(AvsCurrentBrowserTargetFramework)</TargetFramework>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<Nullable>enable</Nullable>
<EmccTotalMemory>16777216</EmccTotalMemory>
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
<BlazorWebAssemblyPreserveCollationData>false</BlazorWebAssemblyPreserveCollationData>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.2" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
<ProjectReference Include="..\..\src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
</ItemGroup>
<Import Project="..\..\build\ReferenceCoreLibraries.props" />
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\src\Browser\Avalonia.Browser\build\Avalonia.Browser.props" />
<Import Project="..\..\src\Browser\Avalonia.Browser\build\Avalonia.Browser.targets" />
</Project>

5
samples/ControlCatalog.Browser.Blazor/Pages/Index.razor

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

39
samples/ControlCatalog.Browser.Blazor/Program.cs

@ -1,39 +0,0 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Browser.Blazor;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using ControlCatalog.Browser.Blazor;
public class Program
{
public static async Task Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
await StartAvaloniaApp();
await host.RunAsync();
}
public static async Task StartAvaloniaApp()
{
await AppBuilder.Configure<ControlCatalog.App>()
.StartBlazorAppAsync();
}
public static WebAssemblyHostBuilder CreateHostBuilder(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
return builder;
}
}

22
samples/ControlCatalog.Browser.Blazor/Properties/launchSettings.json

@ -1,22 +0,0 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:13961",
"sslPort": 44319
}
},
"profiles": {
"ControlCatalog.Web": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

7
samples/ControlCatalog.Browser.Blazor/Shared/MainLayout.razor

@ -1,7 +0,0 @@
@inherits LayoutComponentBase
<div class="page">
<div class="main">
@Body
</div>
</div>

10
samples/ControlCatalog.Browser.Blazor/_Imports.razor

@ -1,10 +0,0 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using ControlCatalog.Browser.Blazor.Shared
@using SkiaSharp

56
samples/ControlCatalog.Browser.Blazor/wwwroot/css/app.css

@ -1,56 +0,0 @@
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
margin: 0;
height: 100vh;
overflow: hidden;
touch-action: none;
}
a, .btn-link {
color: #0366d6;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.content {
padding-top: 1.1rem;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid red;
}
.validation-message {
color: red;
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
#app, .page {
height: 100%;
}

BIN
samples/ControlCatalog.Browser.Blazor/wwwroot/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

22
samples/ControlCatalog.Browser.Blazor/wwwroot/index.html

@ -1,22 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Avalonia Sample</title>
<base href="/" />
<link href="css/app.css" rel="stylesheet" />
</head>
<body>
<div id="app">Powered by Avalonia</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>

35
samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj

@ -1,22 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net461</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<OutputType>WinExe</OutputType>
<TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>../ControlCatalog.NetCore/app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<Compile Include="..\..\src\Avalonia.X11\NativeDialogs\Gtk.cs" Link="NativeControls\Gtk\Gtk.cs" />
<Compile Include="..\..\src\Avalonia.X11\Interop\Glib.cs" Link="NativeControls\Gtk\Glib.cs" />
<Compile Include="..\..\src\Avalonia.Base\Platform\Interop\Utf8Buffer.cs" Link="NativeControls\Utf8Buffer.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="NativeControls\Gtk\nodes.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Include="..\ControlCatalog.NetCore\NativeControls\Win\*.cs" Link="NativeControls\*" />
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\src\Headless\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
<!-- For native controls test -->
<PackageReference Include="MonoMac.NetStandard" Version="0.0.4" />
</ItemGroup>
<PropertyGroup>
<!-- For Microsoft.CodeAnalysis -->
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<Import Project="..\..\build\SampleApp.props" />
<Import Project="..\..\build\NetFX.props" />
<Import Project="..\..\build\ReferenceCoreLibraries.props" />
</Project>

2
samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs → samples/ControlCatalog.Desktop/NativeControls/Gtk/EmbedSample.Gtk.cs

@ -5,7 +5,7 @@ using Avalonia.Controls.Platform;
using System;
using ControlCatalog.Pages;
namespace ControlCatalog.NetCore;
namespace ControlCatalog.Desktop;
public class EmbedSampleGtk : INativeDemoControl
{

2
samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs → samples/ControlCatalog.Desktop/NativeControls/Gtk/GtkHelper.cs

@ -7,7 +7,7 @@ using Avalonia.X11.NativeDialogs;
using static Avalonia.X11.NativeDialogs.Gtk;
using static Avalonia.X11.Interop.Glib;
namespace ControlCatalog.NetCore;
namespace ControlCatalog.Desktop;
internal class GtkHelper
{

0
samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md → samples/ControlCatalog.Desktop/NativeControls/Gtk/nodes-license.md

0
samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4 → samples/ControlCatalog.Desktop/NativeControls/Gtk/nodes.mp4

2
samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs → samples/ControlCatalog.Desktop/NativeControls/Mac/EmbedSample.Mac.cs

@ -8,7 +8,7 @@ using ControlCatalog.Pages;
using MonoMac.Foundation;
using MonoMac.WebKit;
namespace ControlCatalog.NetCore;
namespace ControlCatalog.Desktop;
public class EmbedSampleMac : INativeDemoControl
{

2
samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs → samples/ControlCatalog.Desktop/NativeControls/Mac/MacHelper.cs

@ -3,7 +3,7 @@ using System;
using Avalonia.Controls.Platform;
using MonoMac.AppKit;
namespace ControlCatalog.NetCore;
namespace ControlCatalog.Desktop;
internal class MacHelper
{

2
samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs → samples/ControlCatalog.Desktop/NativeControls/Win/EmbedSample.Win.cs

@ -6,7 +6,7 @@ using Avalonia.Platform;
using ControlCatalog.Pages;
namespace ControlCatalog.NetCore;
namespace ControlCatalog.Desktop;
public class EmbedSampleWin : INativeDemoControl
{

2
samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs → samples/ControlCatalog.Desktop/NativeControls/Win/WinApi.cs

@ -1,7 +1,7 @@
using System;
using System.Runtime.InteropServices;
namespace ControlCatalog.NetCore;
namespace ControlCatalog.Desktop;
internal unsafe class WinApi
{

172
samples/ControlCatalog.Desktop/Program.cs

@ -1,38 +1,180 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Platform;
using ControlCatalog.NetCore;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Fonts.Inter;
using Avalonia.Headless;
using Avalonia.LinuxFramebuffer.Output;
using Avalonia.LogicalTree;
using Avalonia.Rendering.Composition;
using Avalonia.Threading;
using Avalonia.Vulkan;
using ControlCatalog.Pages;
namespace ControlCatalog
namespace ControlCatalog.Desktop
{
internal class Program
static class Program
{
private static bool s_useFramebuffer;
[STAThread]
public static int Main(string[] args)
=> BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
static int Main(string[] args)
{
if (args.Contains("--fbdev"))
{
s_useFramebuffer = true;
}
if (args.Contains("--wait-for-attach"))
{
Console.WriteLine("Attach debugger and use 'Set next statement'");
while (true)
{
Thread.Sleep(100);
if (Debugger.IsAttached)
break;
}
}
var builder = BuildAvaloniaApp();
double GetScaling()
{
var idx = Array.IndexOf(args, "--scaling");
if (idx != 0 && args.Length > idx + 1 &&
double.TryParse(args[idx + 1], NumberStyles.Any, CultureInfo.InvariantCulture, out var scaling))
return scaling;
return 1;
}
if (s_useFramebuffer)
{
SilenceConsole();
return builder.StartLinuxFbDev(args, new FbDevOutputOptions()
{
Scaling = GetScaling()
});
}
else if (args.Contains("--vnc"))
{
return builder.StartWithHeadlessVncPlatform(null, 5901, args, ShutdownMode.OnMainWindowClose);
}
else if (args.Contains("--full-headless"))
{
return builder
.UseHeadless(new AvaloniaHeadlessPlatformOptions
{
UseHeadlessDrawing = true
})
.AfterSetup(_ =>
{
DispatcherTimer.RunOnce(async () =>
{
var window = ((IClassicDesktopStyleApplicationLifetime)Application.Current.ApplicationLifetime)
.MainWindow;
var tc = window.GetLogicalDescendants().OfType<TabControl>().First();
foreach (var page in tc.Items.Cast<TabItem>().ToList())
{
if (page.Header.ToString() == "DatePicker" || page.Header.ToString() == "TreeView")
continue;
Console.WriteLine("Selecting " + page.Header);
tc.SelectedItem = page;
await Task.Delay(50);
}
Console.WriteLine("Selecting the first page");
tc.SelectedItem = tc.Items.OfType<object>().First();
await Task.Delay(500);
Console.WriteLine("Clicked through all pages, triggering GC");
for (var c = 0; c < 3; c++)
{
GC.Collect(2, GCCollectionMode.Forced);
await Task.Delay(50);
}
void FormatMem(string metric, long bytes)
{
Console.WriteLine(metric + ": " + bytes / 1024 / 1024 + "MB");
}
FormatMem("GC allocated bytes", GC.GetTotalMemory(true));
FormatMem("WorkingSet64", Process.GetCurrentProcess().WorkingSet64);
}, TimeSpan.FromSeconds(1));
})
.StartWithClassicDesktopLifetime(args);
}
else if (args.Contains("--drm"))
{
SilenceConsole();
return builder.StartLinuxDrm(args, scaling: GetScaling());
}
else if (args.Contains("--dxgi"))
{
builder.With(new Win32PlatformOptions()
{
CompositionMode = new [] { Win32CompositionMode.LowLatencyDxgiSwapChain }
});
return builder.StartWithClassicDesktopLifetime(args);
}
else
return builder.StartWithClassicDesktopLifetime(args);
}
/// <summary>
/// This method is needed for IDE previewer infrastructure
/// </summary>
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.LogToTrace()
.UsePlatformDetect()
.With(new X11PlatformOptions
{
EnableMultiTouch = true,
UseDBusMenu = true,
EnableIme = true,
})
.With(new VulkanOptions
{
VulkanInstanceCreationOptions = new ()
{
UseDebug = true
}
})
.With(new CompositionOptions()
{
UseRegionDirtyRectClipping = true
})
.UseSkia()
.WithInterFont()
.AfterSetup(builder =>
{
builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions()
if (!s_useFramebuffer)
{
StartupScreenIndex = 1,
});
builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions()
{
StartupScreenIndex = 1,
});
}
EmbedSample.Implementation = new EmbedSampleWin();
EmbedSample.Implementation = OperatingSystem.IsWindows() ? (INativeDemoControl)new EmbedSampleWin()
: OperatingSystem.IsMacOS() ? new EmbedSampleMac()
: OperatingSystem.IsLinux() ? new EmbedSampleGtk()
: null;
})
.UseWin32()
.UseSkia();
.LogToTrace();
private static void ConfigureAssetAssembly(AppBuilder builder)
static void SilenceConsole()
{
AssetLoader.SetDefaultAssembly(typeof(App).Assembly);
new Thread(() =>
{
Console.CursorVisible = false;
while (true)
Console.ReadKey(true);
})
{ IsBackground = true }.Start();
}
}
}

2
samples/ControlCatalog.NetCore/Properties/launchSettings.json → samples/ControlCatalog.Desktop/Properties/launchSettings.json

@ -1,6 +1,6 @@
{
"profiles": {
"ControlCatalog.NetCore": {
"ControlCatalog.Desktop": {
"commandName": "Project"
},
"Dxgi": {

0
samples/ControlCatalog.NetCore/app.manifest → samples/ControlCatalog.Desktop/app.manifest

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

Loading…
Cancel
Save