diff --git a/.editorconfig b/.editorconfig index 8c6ad717e4..7051372383 100644 --- a/.editorconfig +++ b/.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 diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index 44e1bb4696..e91aa1433c 100644 --- a/.nuke/build.schema.json +++ b/.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" } - } -} \ No newline at end of file + ] +} diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf index b021c9f4a5..c9933e5108 100644 --- a/Avalonia.Desktop.slnf +++ b/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", diff --git a/Avalonia.sln b/Avalonia.sln index b4decb7dcc..663f8c3319 100644 --- a/Avalonia.sln +++ b/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} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3ed95005d..8351fc2ea6 100644 --- a/CONTRIBUTING.md +++ b/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) \ No newline at end of file +For more information see the [Contributor Covenant Code of Conduct](https://dotnetfoundation.org/code-of-conduct) diff --git a/Directory.Build.props b/Directory.Build.props index f124456eab..4498f87dab 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,11 +4,11 @@ $(MSBuildThisFileDirectory)build-intermediate/nuget - $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netstandard2.0\Avalonia.Designer.HostApp.dll + $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\$(AvsCurrentTargetFramework)\Avalonia.Designer.HostApp.dll false False - 12 + 14.0 true true true diff --git a/NuGet.Config b/NuGet.Config index 2042fea360..93d7ba8778 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -3,7 +3,19 @@ - - + + + + + + + + + + + + + + diff --git a/api/Avalonia.Android.nupkg.xml b/api/Avalonia.Android.nupkg.xml deleted file mode 100644 index deed9db4de..0000000000 --- a/api/Avalonia.Android.nupkg.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CP0001 - T:Avalonia.Android.AvaloniaMainActivity`1 - baseline/net8.0-android34.0/Avalonia.Android.dll - target/net8.0-android34.0/Avalonia.Android.dll - - - CP0002 - M:Avalonia.Android.AndroidViewControlHandle.get_HandleDescriptor - baseline/net8.0-android34.0/Avalonia.Android.dll - target/net8.0-android34.0/Avalonia.Android.dll - - - CP0007 - T:Avalonia.Android.AndroidViewControlHandle - baseline/net8.0-android34.0/Avalonia.Android.dll - target/net8.0-android34.0/Avalonia.Android.dll - - \ No newline at end of file diff --git a/api/Avalonia.Browser.nupkg.xml b/api/Avalonia.Browser.nupkg.xml deleted file mode 100644 index 0fb414ed14..0000000000 --- a/api/Avalonia.Browser.nupkg.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CP0002 - M:Avalonia.Browser.JSObjectControlHandle.get_Handle - baseline/net8.0-browser1.0/Avalonia.Browser.dll - target/net8.0-browser1.0/Avalonia.Browser.dll - - - CP0002 - M:Avalonia.Browser.JSObjectControlHandle.get_HandleDescriptor - baseline/net8.0-browser1.0/Avalonia.Browser.dll - target/net8.0-browser1.0/Avalonia.Browser.dll - - - CP0007 - T:Avalonia.Browser.JSObjectControlHandle - baseline/net8.0-browser1.0/Avalonia.Browser.dll - target/net8.0-browser1.0/Avalonia.Browser.dll - - \ No newline at end of file diff --git a/api/Avalonia.FreeDesktop.nupkg.xml b/api/Avalonia.FreeDesktop.nupkg.xml deleted file mode 100644 index f5fcb60bc8..0000000000 --- a/api/Avalonia.FreeDesktop.nupkg.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - CP0001 - T:Tmds.DBus.SourceGenerator.PropertyChanges`1 - baseline/netstandard2.0/Avalonia.FreeDesktop.dll - target/netstandard2.0/Avalonia.FreeDesktop.dll - - \ No newline at end of file diff --git a/api/Avalonia.Skia.nupkg.xml b/api/Avalonia.Skia.nupkg.xml deleted file mode 100644 index b275cbff58..0000000000 --- a/api/Avalonia.Skia.nupkg.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - CP0002 - M:Avalonia.Skia.SkiaSharpExtensions.ToSKFilterQuality(Avalonia.Media.Imaging.BitmapInterpolationMode) - baseline/netstandard2.0/Avalonia.Skia.dll - target/netstandard2.0/Avalonia.Skia.dll - - - CP0006 - M:Avalonia.Skia.ISkiaGpuWithPlatformGraphicsContext.TryGetGrContext - baseline/netstandard2.0/Avalonia.Skia.dll - target/netstandard2.0/Avalonia.Skia.dll - - \ No newline at end of file diff --git a/api/Avalonia.Themes.Fluent.nupkg.xml b/api/Avalonia.Themes.Fluent.nupkg.xml deleted file mode 100644 index 717b64f81e..0000000000 --- a/api/Avalonia.Themes.Fluent.nupkg.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - CP0007 - T:Avalonia.Themes.Fluent.ColorPaletteResources - baseline/netstandard2.0/Avalonia.Themes.Fluent.dll - target/netstandard2.0/Avalonia.Themes.Fluent.dll - - - CP0008 - T:Avalonia.Themes.Fluent.ColorPaletteResources - baseline/netstandard2.0/Avalonia.Themes.Fluent.dll - target/netstandard2.0/Avalonia.Themes.Fluent.dll - - \ No newline at end of file diff --git a/api/Avalonia.Win32.nupkg.xml b/api/Avalonia.Win32.nupkg.xml deleted file mode 100644 index 3ce897deda..0000000000 --- a/api/Avalonia.Win32.nupkg.xml +++ /dev/null @@ -1,214 +0,0 @@ - - - - - CP0001 - T:Avalonia.Win32.Interop.Automation.DockPosition - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IDockProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IExpandCollapseProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IGridItemProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IGridProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IInvokeProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IMultipleViewProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IRangeValueProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IRawElementProviderAdviseEvents - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IRawElementProviderFragment - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IRawElementProviderFragmentRoot - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IRawElementProviderSimple - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IRawElementProviderSimple2 - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IScrollItemProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IScrollProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.ISelectionItemProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.ISelectionProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.ISynchronizedInputProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.ITableItemProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.ITableProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.ITextProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.ITextRangeProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IToggleProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.ITransformProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IValueProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.IWindowProvider - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.NavigateDirection - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.ProviderOptions - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.RowOrColumnMajor - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.SupportedTextSelection - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.SynchronizedInputType - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.TextPatternRangeEndpoint - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.TextUnit - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.WindowInteractionState - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - - CP0001 - T:Avalonia.Win32.Interop.Automation.WindowVisualState - baseline/netstandard2.0/Avalonia.Win32.dll - target/netstandard2.0/Avalonia.Win32.dll - - \ No newline at end of file diff --git a/api/Avalonia.iOS.nupkg.xml b/api/Avalonia.iOS.nupkg.xml deleted file mode 100644 index 5f6e822d81..0000000000 --- a/api/Avalonia.iOS.nupkg.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - CP0002 - M:Avalonia.iOS.UIViewControlHandle.get_HandleDescriptor - baseline/net8.0-tvos17.0/Avalonia.iOS.dll - target/net8.0-tvos17.0/Avalonia.iOS.dll - - - CP0007 - T:Avalonia.iOS.UIViewControlHandle - baseline/net8.0-tvos17.0/Avalonia.iOS.dll - target/net8.0-tvos17.0/Avalonia.iOS.dll - - \ No newline at end of file diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index a3ab4977f2..32549d881c 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -1,272 +1,40 @@ - - + + - - CP0001 - T:Avalonia.Controls.PseudolassesExtensions - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Data.Core.CastTypePropertyPathElement - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Data.Core.ChildTraversalPropertyPathElement - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Data.Core.EnsureTypePropertyPathElement - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Data.Core.IPropertyPathElement - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Data.Core.PropertyPath - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Data.Core.PropertyPathBuilder - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Data.Core.PropertyPropertyPathElement - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Utilities.CharacterReader - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Utilities.IdentifierParser - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Utilities.KeywordParser - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0001 - T:Avalonia.Utilities.StyleClassParser - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Diagnostics.AppliedStyle.get_HasActivator - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Diagnostics.AppliedStyle.get_IsActive - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Diagnostics.AppliedStyle.get_Style - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Diagnostics.StyledElementExtensions.GetStyleDiagnostics(Avalonia.StyledElement) - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Diagnostics.StyleDiagnostics.#ctor(System.Collections.Generic.IReadOnlyList{Avalonia.Diagnostics.AppliedStyle}) - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Diagnostics.StyleDiagnostics.get_AppliedStyles - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - CP0002 M:Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant) - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll - - M:Avalonia.Threading.DispatcherPriorityAwaitable.get_IsCompleted - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Threading.DispatcherPriorityAwaitable.GetAwaiter - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Threading.DispatcherPriorityAwaitable.GetResult - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Threading.DispatcherPriorityAwaitable.OnCompleted(System.Action) - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll + M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Threading.DispatcherPriorityAwaitable`1.GetAwaiter - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0002 - M:Avalonia.Threading.DispatcherPriorityAwaitable`1.GetResult - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0002 - 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}) - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll + M:Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0002 - M:Avalonia.Controls.Screens.#ctor(Avalonia.Platform.IScreenImpl) - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll - - - CP0006 - M:Avalonia.Input.Platform.IClipboard.FlushAsync - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Input.Platform.IClipboard.TryGetInProcessDataObjectAsync - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.Storage.IStorageFolder.GetFileAsync(System.String) - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Platform.Storage.IStorageFolder.GetFolderAsync(System.String) - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0006 - M:Avalonia.Controls.Notifications.IManagedNotificationManager.Close(System.Object) - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll - - - CP0006 - M:Avalonia.Controls.Notifications.INotificationManager.Close(Avalonia.Controls.Notifications.INotification) - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll - - - CP0006 - M:Avalonia.Controls.Notifications.INotificationManager.CloseAll - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll - - - CP0006 - M:Avalonia.Controls.Primitives.IPopupHost.ConfigurePosition(Avalonia.Controls.Primitives.PopupPositioning.PopupPositionRequest) - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll - - - CP0006 - M:Avalonia.Controls.Primitives.IPopupHost.TakeFocus - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll + M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(Avalonia.Platform.PlatformThemeVariant) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll CP0006 - P:Avalonia.Controls.Platform.IInsetsManager.DisplayEdgeToEdgePreference - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll + M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(System.Nullable{Avalonia.Platform.PlatformThemeVariant}) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll CP0006 - P:Avalonia.Controls.Platform.IInsetsManager.DisplaysEdgeToEdge - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll - - - CP0007 - T:Avalonia.Threading.DispatcherPriorityAwaitable - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0007 - T:Avalonia.Threading.DispatcherPriorityAwaitable`1 - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0008 - T:Avalonia.Threading.DispatcherPriorityAwaitable - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0008 - T:Avalonia.Threading.DispatcherPriorityAwaitable`1 - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0009 - T:Avalonia.Diagnostics.StyleDiagnostics - baseline/netstandard2.0/Avalonia.Base.dll - target/netstandard2.0/Avalonia.Base.dll - - - CP0009 - T:Avalonia.Controls.Screens - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll - - - CP0012 - M:Avalonia.Controls.Button.OnAccessKey(Avalonia.Interactivity.RoutedEventArgs) - baseline/netstandard2.0/Avalonia.Controls.dll - target/netstandard2.0/Avalonia.Controls.dll + M:Avalonia.Platform.ITopLevelImpl.SetFrameThemeVariant(System.Nullable{Avalonia.Platform.PlatformThemeVariant}) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll - \ No newline at end of file + diff --git a/azure-pipelines-integrationtests.yml b/azure-pipelines-integrationtests.yml index fa83e8bccd..e1e037ed3e 100644 --- a/azure-pipelines-integrationtests.yml +++ b/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' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d9a4c80fdf..06da4bcd2b 100644 --- a/azure-pipelines.yml +++ b/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' diff --git a/build/AnalyzerProject.targets b/build/AnalyzerProject.targets index f067ec0418..a67487950d 100644 --- a/build/AnalyzerProject.targets +++ b/build/AnalyzerProject.targets @@ -6,7 +6,7 @@ - + diff --git a/build/Base.props b/build/Base.props index 0e667f105b..ab5853fcfa 100644 --- a/build/Base.props +++ b/build/Base.props @@ -1,12 +1,8 @@  - - - - - - - + - + + + diff --git a/build/DevSingleProject.targets b/build/DevSingleProject.targets index f6b9b54d02..6387123429 100644 --- a/build/DevSingleProject.targets +++ b/build/DevSingleProject.targets @@ -9,8 +9,6 @@ - - diff --git a/build/HarfBuzzSharp.props b/build/HarfBuzzSharp.props index 8f457eb46e..c7a3d753f2 100644 --- a/build/HarfBuzzSharp.props +++ b/build/HarfBuzzSharp.props @@ -1,7 +1,7 @@  - - - + + + diff --git a/build/ImageSharp.props b/build/ImageSharp.props index 4f04171a9a..cf401630b0 100644 --- a/build/ImageSharp.props +++ b/build/ImageSharp.props @@ -1,5 +1,5 @@ - + diff --git a/build/JetBrains.dotMemoryUnit.props b/build/JetBrains.dotMemoryUnit.props deleted file mode 100644 index 5d74d474cf..0000000000 --- a/build/JetBrains.dotMemoryUnit.props +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/build/Microsoft.Reactive.Testing.props b/build/Microsoft.Reactive.Testing.props index a0ba2163bb..c39c72df77 100644 --- a/build/Microsoft.Reactive.Testing.props +++ b/build/Microsoft.Reactive.Testing.props @@ -1,5 +1,5 @@  - + diff --git a/build/Moq.props b/build/Moq.props index 357f0c9a5f..fc659f7f5f 100644 --- a/build/Moq.props +++ b/build/Moq.props @@ -1,5 +1,5 @@  - + diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props deleted file mode 100644 index 299a91953c..0000000000 --- a/build/ReactiveUI.props +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/build/Rx.props b/build/Rx.props index 73b18c6e0b..462428c286 100644 --- a/build/Rx.props +++ b/build/Rx.props @@ -1,5 +1,5 @@  - + diff --git a/build/SharedVersion.props b/build/SharedVersion.props index d18aa6447c..37d14a5647 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -3,7 +3,6 @@ Avalonia 12.0.999 - 11.1.0 Avalonia Team Copyright 2013-$([System.DateTime]::Now.ToString(`yyyy`)) © The AvaloniaUI Project https://avaloniaui.net/?utm_source=nuget&utm_medium=referral&utm_content=project_homepage_link diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index 74339fb125..0dc94b3239 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,7 +1,7 @@  - - - + + + diff --git a/build/SourceLink.props b/build/SourceLink.props index fb014e7e21..62ad50e78d 100644 --- a/build/SourceLink.props +++ b/build/SourceLink.props @@ -14,10 +14,6 @@ true - - - - diff --git a/build/TargetFrameworks.props b/build/TargetFrameworks.props index e48c9933f3..96541e7ecf 100644 --- a/build/TargetFrameworks.props +++ b/build/TargetFrameworks.props @@ -1,19 +1,17 @@ - net8.0 + net10.0 $(AvsCurrentTargetFramework)-windows $(AvsCurrentTargetFramework)-macos - $(AvsCurrentTargetFramework)-android34.0 - $(AvsCurrentTargetFramework)-maccatalyst17.0 - $(AvsCurrentTargetFramework)-ios17.0 - $(AvsCurrentTargetFramework)-tvos17.0 + $(AvsCurrentTargetFramework)-android36.0 + $(AvsCurrentTargetFramework)-maccatalyst26.0 + $(AvsCurrentTargetFramework)-ios26.0 + $(AvsCurrentTargetFramework)-tvos26.0 $(AvsCurrentTargetFramework)-browser - $(AvsCurrentTargetFramework)-tizen - 8.0.155 - net6.0 - net6.0-windows + net8.0 + net8.0-windows @@ -22,8 +20,7 @@ 13.0 13.0 13.1 - 6.5 - 21.0 + 24.0 10.15 diff --git a/build/UnitTests.NetCore.targets b/build/UnitTests.NetCore.targets index 42da8e4ab1..19b2253f4f 100644 --- a/build/UnitTests.NetCore.targets +++ b/build/UnitTests.NetCore.targets @@ -3,7 +3,4 @@ false true - - - - \ No newline at end of file + diff --git a/build/XUnit.props b/build/XUnit.props index b4e9708ecd..a5579ed761 100644 --- a/build/XUnit.props +++ b/build/XUnit.props @@ -1,14 +1,14 @@  - - - - - - - - - + + + + + + + + + $(MSBuildThisFileDirectory)\avalonia.snk diff --git a/dirs.proj b/dirs.proj index 28a91c8b68..084fd30475 100644 --- a/dirs.proj +++ b/dirs.proj @@ -9,23 +9,17 @@ + + - - - - - - - - diff --git a/docs/api-compat.md b/docs/api-compat.md index 1aa4fbd422..17dc42b38c 100644 --- a/docs/api-compat.md +++ b/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 \ No newline at end of file +- [API Validation Tool Implementation](https://github.com/AvaloniaUI/Avalonia/pull/12072) - Pull request that introduced this feature diff --git a/docs/build.md b/docs/build.md index d54bdeee20..cef5ed3e10 100644 --- a/docs/build.md +++ b/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) \ No newline at end of file +See [Building Local NuGet Packages](nuget.md) diff --git a/docs/porting-code-from-3rd-party-sources.md b/docs/porting-code-from-3rd-party-sources.md index 9aa72b8fce..249c215545 100644 --- a/docs/porting-code-from-3rd-party-sources.md +++ b/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. \ No newline at end of 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. diff --git a/external/XamlX b/external/XamlX index 83567b8a50..c32d3040e5 160000 --- a/external/XamlX +++ b/external/XamlX @@ -1 +1 @@ -Subproject commit 83567b8a50bbf612a0b1420a3dc6d8e8ebee2399 +Subproject commit c32d3040e536ae9768233ea5a445697632578bd0 diff --git a/global.json b/global.json index f496cffc50..18b3e3be29 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.411", + "version": "10.0.101", "rollForward": "latestFeature" }, "msbuild-sdks": { diff --git a/native/Avalonia.Native/inc/com.h b/native/Avalonia.Native/inc/com.h index 42a989e050..9a3f067dce 100644 --- a/native/Avalonia.Native/inc/com.h +++ b/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 { diff --git a/native/Avalonia.Native/inc/noarc.h b/native/Avalonia.Native/inc/noarc.h index c49d975ade..7d338bf05d 100644 --- a/native/Avalonia.Native/inc/noarc.h +++ b/native/Avalonia.Native/inc/noarc.h @@ -8,4 +8,7 @@ public: ~CppAutoreleasePool(); }; -#define START_ARP_CALL CppAutoreleasePool __autoreleasePool \ No newline at end of file +#define START_ARP_CALL CppAutoreleasePool __autoreleasePool +extern void ReleaseNSObject(void* obj); +extern void RetainNSObject(void* obj); +extern uint64_t GetRetainCountForNSObject(void* obj); diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 9a67ee0161..e82c150961 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/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 = ""; }; 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 = ""; }; + 1AC7F1422DCA0C2E003A161B /* crapium.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objcpp; path = crapium.mm; sourceTree = ""; }; + 1AC7F1442DCA0D6A003A161B /* crapium.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = crapium.h; sourceTree = ""; }; + 1AE55B8B2DC1060E00FD0BB3 /* memhelp.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = memhelp.mm; sourceTree = ""; }; 1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = ""; }; 37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = ""; }; 379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = ""; }; @@ -130,6 +136,7 @@ EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformSettings.mm; sourceTree = ""; }; F10084832BFF1F9E0024303E /* TopLevelImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopLevelImpl.h; sourceTree = ""; }; F10084852BFF1FB40024303E /* TopLevelImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TopLevelImpl.mm; sourceTree = ""; }; + F931F8672E2D43A4004E081E /* clipboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = clipboard.h; sourceTree = ""; }; /* 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; diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h index c80805a15c..030330c908 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.h +++ b/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; diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index 4cc495f321..0da6f43bf4 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/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 )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(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; diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 03daa2f296..6d49fc85e0 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/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(); - 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 diff --git a/native/Avalonia.Native/src/OSX/KeyTransform.mm b/native/Avalonia.Native/src/OSX/KeyTransform.mm index ba3d809dd9..a6056cef91 100644 --- a/native/Avalonia.Native/src/OSX/KeyTransform.mm +++ b/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 }, diff --git a/native/Avalonia.Native/src/OSX/StorageProvider.mm b/native/Avalonia.Native/src/OSX/StorageProvider.mm index 92278a85e9..570281fdfd 100644 --- a/native/Avalonia.Native/src/OSX/StorageProvider.mm +++ b/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 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. diff --git a/native/Avalonia.Native/src/OSX/TopLevelImpl.h b/native/Avalonia.Native/src/OSX/TopLevelImpl.h index dd494ab761..0be1439ec2 100644 --- a/native/Avalonia.Native/src/OSX/TopLevelImpl.h +++ b/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(); diff --git a/native/Avalonia.Native/src/OSX/TopLevelImpl.mm b/native/Avalonia.Native/src/OSX/TopLevelImpl.mm index 6200f096d3..bdfc1be62f 100644 --- a/native/Avalonia.Native/src/OSX/TopLevelImpl.mm +++ b/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 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]; } diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 0c3d410094..873a520d6d 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/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(); diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 6553e8f460..551ea4c2e0 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/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; } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index 7b911ef945..940699f09d 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/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; diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 1847a83160..30e1f5ea00 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/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()) diff --git a/native/Avalonia.Native/src/OSX/cgl.mm b/native/Avalonia.Native/src/OSX/cgl.mm index 085037978e..4fc4064df1 100644 --- a/native/Avalonia.Native/src/OSX/cgl.mm +++ b/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); diff --git a/native/Avalonia.Native/src/OSX/clipboard.h b/native/Avalonia.Native/src/OSX/clipboard.h new file mode 100644 index 0000000000..fd0ddbf117 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/clipboard.h @@ -0,0 +1,7 @@ +#pragma once + +#include "common.h" + +@interface WriteableClipboardItem : NSObject +- (nonnull instancetype) initWithItem:(nonnull IAvnClipboardDataItem*)item source:(nonnull IAvnClipboardDataSource*)source; +@end diff --git a/native/Avalonia.Native/src/OSX/clipboard.mm b/native/Avalonia.Native/src/OSX/clipboard.mm index 68f0e7d87a..9786a64b27 100644 --- a/native/Avalonia.Native/src/OSX/clipboard.mm +++ b/native/Avalonia.Native/src/OSX/clipboard.mm @@ -1,206 +1,354 @@ +#import #include "common.h" +#include "clipboard.h" #include "AvnString.h" class Clipboard : public ComSingleObject { 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 *types) + { + if (types != nil) { - if(ppv == nullptr) + NSMutableArray *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* 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* 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 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*) 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(cb); - if(clipboard == nil) + if ([type isEqualToString:GetAvnCustomDataType()]) + return @""; + + ComPtr 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 diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 36c157704d..fae03984fd 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/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* CreateDraggingSource(NSDragOperation op, IAvnDndResultCallback* cb, void* handle); extern void* GetAvnDataObjectHandleFromDraggingInfo(NSObject* 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 diff --git a/native/Avalonia.Native/src/OSX/crapium.h b/native/Avalonia.Native/src/OSX/crapium.h new file mode 100644 index 0000000000..0b27b68c79 --- /dev/null +++ b/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 +@protocol MTLSharedEvent; + +API_AVAILABLE(macos(12)) +extern BOOL MtlSharedEventWaitUntilSignaledValueHack(id ev, uint64_t value, uint64_t milliseconds); +#endif /* crapium_h */ diff --git a/native/Avalonia.Native/src/OSX/crapium.mm b/native/Avalonia.Native/src/OSX/crapium.mm new file mode 100644 index 0000000000..962d7e2eda --- /dev/null +++ b/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 +#import "crapium.h" +@class MTLSharedEventHandle; +@protocol MTLSharedEvent; +@protocol MTLEvent; + +typedef void (^MTLSharedEventNotificationBlock)(id , uint64_t value); + +API_AVAILABLE(macos(10.14), ios(12.0)) +@protocol MTLSharedEvent +// 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 ev, uint64_t value, uint64_t milliseconds) +{ + return [ev waitUntilSignaledValue:value timeoutMS:milliseconds]; +} diff --git a/native/Avalonia.Native/src/OSX/dnd.mm b/native/Avalonia.Native/src/OSX/dnd.mm index 531bdcccfd..aebe4afb88 100644 --- a/native/Avalonia.Native/src/OSX/dnd.mm +++ b/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 diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 0e3621517e..2a92eb3bcf 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/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() diff --git a/native/Avalonia.Native/src/OSX/memhelp.mm b/native/Avalonia.Native/src/OSX/memhelp.mm new file mode 100644 index 0000000000..1efee567b4 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/memhelp.mm @@ -0,0 +1,40 @@ +#include "common.h" +class MemHelper : public ComSingleObject +{ + 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(); +} diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index 1235979cb2..1adbe093bb 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/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]; diff --git a/native/Avalonia.Native/src/OSX/metal.mm b/native/Avalonia.Native/src/OSX/metal.mm index 5622f2040e..33aa2aeb53 100644 --- a/native/Avalonia.Native/src/OSX/metal.mm +++ b/native/Avalonia.Native/src/OSX/metal.mm @@ -3,6 +3,74 @@ #import #include "common.h" #include "rendertarget.h" +#import "crapium.h" + + +class API_AVAILABLE(macos(12.0)) AvnMTLSharedEvent : public ComSingleObject +{ + id _event; +public: + + AvnMTLSharedEvent(id ev) : _event(ev) + { + + } + + FORWARD_IUNKNOWN() + + id 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 +{ + id _texture; +public: + FORWARD_IUNKNOWN() + AvnMetalTexture(id 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 { @@ -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)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(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 device, id 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)object; + + if(evId == nil) + return nil; + + + return new AvnMTLSharedEvent(evId); + } + else + { + return nil; + } +} diff --git a/native/Avalonia.Native/src/OSX/noarc.mm b/native/Avalonia.Native/src/OSX/noarc.mm index 82378ce84c..6a87fc78d0 100644 --- a/native/Avalonia.Native/src/OSX/noarc.mm +++ b/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]; +} diff --git a/native/Avalonia.Native/src/OSX/trayicon.mm b/native/Avalonia.Native/src/OSX/trayicon.mm index 917ff87694..3ac42b4f25 100644 --- a/native/Avalonia.Native/src/OSX/trayicon.mm +++ b/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]; diff --git a/nukebuild/ApiDiffHelper.cs b/nukebuild/ApiDiffHelper.cs index ac6be61ee3..4a51630557 100644 --- a/nukebuild/ApiDiffHelper.cs +++ b/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)>(.*?)", 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(); + + 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) + /// + /// 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 '/'. + /// + 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}"; + }); + + 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(); + + // 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 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(); - var right = new List(); + 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 outputs) + => outputs + .Where(output => output.Type == OutputType.Err) + .Select(output => output.Text) + .ToArray(); + + static void ThrowOnErrors(List 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 DownloadAndExtractPackagesAsync( + IEnumerable 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(); + + foreach (var packagePath in currentPackagePaths) + { + string packageId; + AbsolutePath currentFolderPath; + AbsolutePath baselineFolderPath; + Dictionary currentFolderNames; + Dictionary 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(); + + 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 GetDlls(ZipArchive archive) + static async Task 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(); + return new NuGetDownloadContext(packageSource, findPackageByIdResource); } - static async Task DownloadBaselinePackage(string packagePath, string baselineVersion) + /// + /// 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. + /// + static async Task 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 ExtractDiffableAssembliesFromPackage( + ZipArchive packageArchive, + AbsolutePath destinationFolderPath) + { + var folderByFramework = new Dictionary(); - 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 ExtractDll(string basePath, DllEntry dllEntry, string targetFolder) + public sealed class GlobalDiffInfo( + NuGetVersion baselineVersion, + NuGetVersion currentVersion, + ImmutableArray 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 Packages { get; } = packages; + } - return dllPath; + public sealed class PackageDiffInfo(string packageId, ImmutableArray frameworks) + { + public string PackageId { get; } = packageId; + public ImmutableArray 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; } } diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index c6942e430c..1c8b3d37ed 100644 --- a/nukebuild/Build.cs +++ b/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("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 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 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( diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index 41e075a64c..4b55e1691e 100644 --- a/nukebuild/BuildParameters.cs +++ b/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 BuildDirs { get; } + public List 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; } } diff --git a/nukebuild/BuildTasksPatcher.cs b/nukebuild/BuildTasksPatcher.cs index 6bb71f4320..874d663490 100644 --- a/nukebuild/BuildTasksPatcher.cs +++ b/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 diff --git a/nukebuild/ByteArrayEqualityComparer.cs b/nukebuild/ByteArrayEqualityComparer.cs new file mode 100644 index 0000000000..f49a8f830e --- /dev/null +++ b/nukebuild/ByteArrayEqualityComparer.cs @@ -0,0 +1,25 @@ +#nullable enable + +using System; +using System.Collections.Generic; + +public sealed class ByteArrayEqualityComparer : IEqualityComparer +{ + 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(); + } +} diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 2a74be30bf..36a948ec4e 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -9,25 +9,17 @@ $(AvsCurrentTargetFramework) true - - https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8-transport/nuget/v3/index.json - - - + - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + + diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index b954f02f47..1cd1245d3f 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -1,11 +1,11 @@  - $(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0 + $(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks) Avalonia - + @@ -27,13 +27,13 @@ - <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/netstandard2.0/Avalonia.Designer.HostApp.dll"> - tools/netstandard2.0/designer + <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/$(AvsCurrentTargetFramework)/Avalonia.Designer.HostApp.dll"> + tools/$(AvsCurrentTargetFramework)/designer false None - <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/net461/Avalonia.Designer.HostApp.exe"> - tools/net461/designer + <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/$(AvsLegacyTargetFrameworks)/Avalonia.Designer.HostApp.dll"> + tools/$(AvsLegacyTargetFrameworks)/designer false None diff --git a/packages/Avalonia/Avalonia.props b/packages/Avalonia/Avalonia.props index 57b7f9dc32..78656a1726 100644 --- a/packages/Avalonia/Avalonia.props +++ b/packages/Avalonia/Avalonia.props @@ -1,12 +1,11 @@ - $(MSBuildThisFileDirectory)\..\tools\netstandard2.0\designer\Avalonia.Designer.HostApp.dll - $(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe + $(MSBuildThisFileDirectory)\..\tools\$(AvsCurrentTargetFramework)\designer\Avalonia.Designer.HostApp.dll $(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll $(UsedAvaloniaProducts);AvaloniaUI true false - false + true true diff --git a/packages/Avalonia/AvaloniaPrivateApis.targets b/packages/Avalonia/AvaloniaPrivateApis.targets index 5f69187d92..47219535cc 100644 --- a/packages/Avalonia/AvaloniaPrivateApis.targets +++ b/packages/Avalonia/AvaloniaPrivateApis.targets @@ -13,7 +13,7 @@ - net6.0 + net8.0 netstandard2.0 diff --git a/packages/Avalonia/AvaloniaSingleProject.targets b/packages/Avalonia/AvaloniaSingleProject.targets index 17b456f145..25125b26e4 100644 --- a/packages/Avalonia/AvaloniaSingleProject.targets +++ b/packages/Avalonia/AvaloniaSingleProject.targets @@ -98,7 +98,7 @@ 13.0 10.15 13.1 - 21.0 + 24.0 6.5 @@ -278,7 +278,7 @@ Include="$(TizenSharedPrefix)\**\*" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);@(TizenTpkUserExcludeFiles)" /> - + diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 958725eed5..f60ba69395 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -26,7 +26,7 @@ - + diff --git a/samples/ControlCatalog.Browser.Blazor/App.razor b/samples/ControlCatalog.Browser.Blazor/App.razor deleted file mode 100644 index b941644e29..0000000000 --- a/samples/ControlCatalog.Browser.Blazor/App.razor +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - -

Sorry, there's nothing at this address.

-
-
-
diff --git a/samples/ControlCatalog.Browser.Blazor/App.razor.cs b/samples/ControlCatalog.Browser.Blazor/App.razor.cs deleted file mode 100644 index c331625664..0000000000 --- a/samples/ControlCatalog.Browser.Blazor/App.razor.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Threading.Tasks; -using Avalonia; -using Avalonia.Browser.Blazor; - -namespace ControlCatalog.Browser.Blazor; - -public partial class App -{ -} diff --git a/samples/ControlCatalog.Browser.Blazor/ControlCatalog.Browser.Blazor.csproj b/samples/ControlCatalog.Browser.Blazor/ControlCatalog.Browser.Blazor.csproj deleted file mode 100644 index 2950028e68..0000000000 --- a/samples/ControlCatalog.Browser.Blazor/ControlCatalog.Browser.Blazor.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - $(AvsCurrentBrowserTargetFramework) - browser-wasm - enable - 16777216 - false - false - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/ControlCatalog.Browser.Blazor/Pages/Index.razor b/samples/ControlCatalog.Browser.Blazor/Pages/Index.razor deleted file mode 100644 index 7480e4c5e9..0000000000 --- a/samples/ControlCatalog.Browser.Blazor/Pages/Index.razor +++ /dev/null @@ -1,5 +0,0 @@ -@page "/" - -@using Avalonia.Browser.Blazor - - diff --git a/samples/ControlCatalog.Browser.Blazor/Program.cs b/samples/ControlCatalog.Browser.Blazor/Program.cs deleted file mode 100644 index e68e9b14d9..0000000000 --- a/samples/ControlCatalog.Browser.Blazor/Program.cs +++ /dev/null @@ -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() - .StartBlazorAppAsync(); - } - - public static WebAssemblyHostBuilder CreateHostBuilder(string[] args) - { - var builder = WebAssemblyHostBuilder.CreateDefault(args); - - builder.RootComponents.Add("#app"); - - builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); - - return builder; - } -} - - - - diff --git a/samples/ControlCatalog.Browser.Blazor/Properties/launchSettings.json b/samples/ControlCatalog.Browser.Blazor/Properties/launchSettings.json deleted file mode 100644 index ad2b1e30f6..0000000000 --- a/samples/ControlCatalog.Browser.Blazor/Properties/launchSettings.json +++ /dev/null @@ -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" - } - } - } -} diff --git a/samples/ControlCatalog.Browser.Blazor/Shared/MainLayout.razor b/samples/ControlCatalog.Browser.Blazor/Shared/MainLayout.razor deleted file mode 100644 index 63fb17716c..0000000000 --- a/samples/ControlCatalog.Browser.Blazor/Shared/MainLayout.razor +++ /dev/null @@ -1,7 +0,0 @@ -@inherits LayoutComponentBase - -
-
- @Body -
-
diff --git a/samples/ControlCatalog.Browser.Blazor/_Imports.razor b/samples/ControlCatalog.Browser.Blazor/_Imports.razor deleted file mode 100644 index dc4f778352..0000000000 --- a/samples/ControlCatalog.Browser.Blazor/_Imports.razor +++ /dev/null @@ -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 diff --git a/samples/ControlCatalog.Browser.Blazor/wwwroot/css/app.css b/samples/ControlCatalog.Browser.Blazor/wwwroot/css/app.css deleted file mode 100644 index 49ca14e162..0000000000 --- a/samples/ControlCatalog.Browser.Blazor/wwwroot/css/app.css +++ /dev/null @@ -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%; -} diff --git a/samples/ControlCatalog.Browser.Blazor/wwwroot/favicon.ico b/samples/ControlCatalog.Browser.Blazor/wwwroot/favicon.ico deleted file mode 100644 index da8d49ff9b..0000000000 Binary files a/samples/ControlCatalog.Browser.Blazor/wwwroot/favicon.ico and /dev/null differ diff --git a/samples/ControlCatalog.Browser.Blazor/wwwroot/index.html b/samples/ControlCatalog.Browser.Blazor/wwwroot/index.html deleted file mode 100644 index cad9123836..0000000000 --- a/samples/ControlCatalog.Browser.Blazor/wwwroot/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - Avalonia Sample - - - - - -
Powered by Avalonia
- -
- An unhandled error has occurred. - Reload - 🗙 -
- - - diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj index 2c47effa38..9398987837 100644 --- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj +++ b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj @@ -1,22 +1,41 @@  - Exe - net461 - x64 + WinExe + $(AvsCurrentTargetFramework) + true true - ../ControlCatalog.NetCore/app.manifest - - + + + + + + + + PreserveNewest + - + + + + + + + + + + + en + app.manifest + + - + diff --git a/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs b/samples/ControlCatalog.Desktop/NativeControls/Gtk/EmbedSample.Gtk.cs similarity index 96% rename from samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs rename to samples/ControlCatalog.Desktop/NativeControls/Gtk/EmbedSample.Gtk.cs index 81a5ba536f..c42ac73b88 100644 --- a/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs +++ b/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 { diff --git a/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs b/samples/ControlCatalog.Desktop/NativeControls/Gtk/GtkHelper.cs similarity index 97% rename from samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs rename to samples/ControlCatalog.Desktop/NativeControls/Gtk/GtkHelper.cs index 9d698716ee..78d7a45154 100644 --- a/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs +++ b/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 { diff --git a/samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md b/samples/ControlCatalog.Desktop/NativeControls/Gtk/nodes-license.md similarity index 100% rename from samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md rename to samples/ControlCatalog.Desktop/NativeControls/Gtk/nodes-license.md diff --git a/samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4 b/samples/ControlCatalog.Desktop/NativeControls/Gtk/nodes.mp4 similarity index 100% rename from samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4 rename to samples/ControlCatalog.Desktop/NativeControls/Gtk/nodes.mp4 diff --git a/samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs b/samples/ControlCatalog.Desktop/NativeControls/Mac/EmbedSample.Mac.cs similarity index 95% rename from samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs rename to samples/ControlCatalog.Desktop/NativeControls/Mac/EmbedSample.Mac.cs index 7967c9c073..b761ce0972 100644 --- a/samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs +++ b/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 { diff --git a/samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs b/samples/ControlCatalog.Desktop/NativeControls/Mac/MacHelper.cs similarity index 95% rename from samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs rename to samples/ControlCatalog.Desktop/NativeControls/Mac/MacHelper.cs index 5b3bc9abf1..154878e4ff 100644 --- a/samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs +++ b/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 { diff --git a/samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs b/samples/ControlCatalog.Desktop/NativeControls/Win/EmbedSample.Win.cs similarity index 98% rename from samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs rename to samples/ControlCatalog.Desktop/NativeControls/Win/EmbedSample.Win.cs index 77982db0ca..c529c093f6 100644 --- a/samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs +++ b/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 { diff --git a/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs b/samples/ControlCatalog.Desktop/NativeControls/Win/WinApi.cs similarity index 98% rename from samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs rename to samples/ControlCatalog.Desktop/NativeControls/Win/WinApi.cs index bd0c1bf98e..1bb3879ac3 100644 --- a/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs +++ b/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 { diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index 533ee8308a..707ead5f1d 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/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().First(); + foreach (var page in tc.Items.Cast().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().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); + } /// /// This method is needed for IDE previewer infrastructure /// public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() - .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(); } } } diff --git a/samples/ControlCatalog.NetCore/Properties/launchSettings.json b/samples/ControlCatalog.Desktop/Properties/launchSettings.json similarity index 87% rename from samples/ControlCatalog.NetCore/Properties/launchSettings.json rename to samples/ControlCatalog.Desktop/Properties/launchSettings.json index 11feb94bb3..d57a2680e4 100644 --- a/samples/ControlCatalog.NetCore/Properties/launchSettings.json +++ b/samples/ControlCatalog.Desktop/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "ControlCatalog.NetCore": { + "ControlCatalog.Desktop": { "commandName": "Project" }, "Dxgi": { diff --git a/samples/ControlCatalog.NetCore/app.manifest b/samples/ControlCatalog.Desktop/app.manifest similarity index 100% rename from samples/ControlCatalog.NetCore/app.manifest rename to samples/ControlCatalog.Desktop/app.manifest diff --git a/samples/ControlCatalog.iOS/ControlCatalog.MacCatalyst.csproj b/samples/ControlCatalog.MacCatalyst/ControlCatalog.MacCatalyst.csproj similarity index 56% rename from samples/ControlCatalog.iOS/ControlCatalog.MacCatalyst.csproj rename to samples/ControlCatalog.MacCatalyst/ControlCatalog.MacCatalyst.csproj index f7cd8eebf4..954ed20d99 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.MacCatalyst.csproj +++ b/samples/ControlCatalog.MacCatalyst/ControlCatalog.MacCatalyst.csproj @@ -1,19 +1,22 @@  + Exe manual $(AvsCurrentMacCatalystTargetFramework) - 14.0 + 15.0 + true + - - - - Info.plist - + + - - true - + + + + + + diff --git a/samples/ControlCatalog.MacCatalyst/Entitlements.plist b/samples/ControlCatalog.MacCatalyst/Entitlements.plist new file mode 100644 index 0000000000..0c67376eba --- /dev/null +++ b/samples/ControlCatalog.MacCatalyst/Entitlements.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/samples/ControlCatalog.iOS/Info.Catalyst.plist b/samples/ControlCatalog.MacCatalyst/Info.plist similarity index 96% rename from samples/ControlCatalog.iOS/Info.Catalyst.plist rename to samples/ControlCatalog.MacCatalyst/Info.plist index ad5aecb10a..1a21cbbfed 100644 --- a/samples/ControlCatalog.iOS/Info.Catalyst.plist +++ b/samples/ControlCatalog.MacCatalyst/Info.plist @@ -3,7 +3,7 @@ CFBundleDisplayName - ControlCatalog.Catalyst + ControlCatalog.MacCatalyst CFBundleIdentifier Avalonia.ControlCatalog CFBundleShortVersionString diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj deleted file mode 100644 index 9398987837..0000000000 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - WinExe - $(AvsCurrentTargetFramework) - true - true - - - - - - - - - - - PreserveNewest - - - - - - - - - - - - - - - - - en - app.manifest - - - - - diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs deleted file mode 100644 index 036dd13f7a..0000000000 --- a/samples/ControlCatalog.NetCore/Program.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Avalonia; -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.NetCore -{ - static class Program - { - private static bool s_useFramebuffer; - - [STAThread] - 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().First(); - foreach (var page in tc.Items.Cast().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().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); - } - - /// - /// This method is needed for IDE previewer infrastructure - /// - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .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 => - { - if (!s_useFramebuffer) - { - builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions() - { - StartupScreenIndex = 1, - }); - } - - EmbedSample.Implementation = OperatingSystem.IsWindows() ? (INativeDemoControl)new EmbedSampleWin() - : OperatingSystem.IsMacOS() ? new EmbedSampleMac() - : OperatingSystem.IsLinux() ? new EmbedSampleGtk() - : null; - }) - .LogToTrace(); - - static void SilenceConsole() - { - new Thread(() => - { - Console.CursorVisible = false; - while (true) - Console.ReadKey(true); - }) - { IsBackground = true }.Start(); - } - } -} diff --git a/samples/ControlCatalog.Tizen/ControlCatalog.Tizen.csproj b/samples/ControlCatalog.Tizen/ControlCatalog.Tizen.csproj deleted file mode 100644 index 4b12a096d4..0000000000 --- a/samples/ControlCatalog.Tizen/ControlCatalog.Tizen.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - $(AvsCurrentTizenTargetFramework) - Exe - - - - - - - - - - - - - - - - - - - - diff --git a/samples/ControlCatalog.Tizen/EmbedSampleNuiTizen.cs b/samples/ControlCatalog.Tizen/EmbedSampleNuiTizen.cs deleted file mode 100644 index 728ea58e3b..0000000000 --- a/samples/ControlCatalog.Tizen/EmbedSampleNuiTizen.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Avalonia.Platform; -using Avalonia.Tizen; -using ControlCatalog.Pages; -using Tizen.NUI.BaseComponents; -using Tizen.NUI.Components; -using Tizen.Pims.Contacts.ContactsViews; - -namespace ControlCatalog.Tizen; -public class EmbedSampleNuiTizen : INativeDemoControl -{ - public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) - { - if (isSecond) - { - var webView = new WebView(); - webView.LoadUrl("https://avaloniaui.net/"); - return new NuiViewControlHandle(webView); - } - else - { - var clickCount = 0; - var button = new Button - { - Text = "Hello world" - }; - - button.Clicked += (sender, e) => - { - clickCount++; - button.Text = $"Click count {clickCount}"; - }; - - return new NuiViewControlHandle(button); - } - } -} diff --git a/samples/ControlCatalog.Tizen/Main.cs b/samples/ControlCatalog.Tizen/Main.cs deleted file mode 100644 index 2d611ef5a3..0000000000 --- a/samples/ControlCatalog.Tizen/Main.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Avalonia; -using Avalonia.Tizen; -using ElmSharp; -using SkiaSharp; -using Tizen.Applications; - -namespace ControlCatalog.Tizen; - -class Program : NuiTizenApplication -{ - protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) => - base.CustomizeAppBuilder(builder).AfterSetup(_ => - { - Pages.EmbedSample.Implementation = new EmbedSampleNuiTizen(); - }); - - static void Main(string[] args) - { - var app = new Program(); - app.Run(args); - } -} diff --git a/samples/ControlCatalog.Tizen/shared/res/Avalonia.png b/samples/ControlCatalog.Tizen/shared/res/Avalonia.png deleted file mode 100644 index ea0bb4986f..0000000000 Binary files a/samples/ControlCatalog.Tizen/shared/res/Avalonia.png and /dev/null differ diff --git a/samples/ControlCatalog.Tizen/tizen-manifest.xml b/samples/ControlCatalog.Tizen/tizen-manifest.xml deleted file mode 100644 index 50101d37ff..0000000000 --- a/samples/ControlCatalog.Tizen/tizen-manifest.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - Avalonia.png - - - - - - http://tizen.org/privilege/appdir.shareddata - http://tizen.org/privilege/appmanager.launch - http://tizen.org/privilege/externalstorage - http://tizen.org/privilege/externalstorage.appdata - http://tizen.org/privilege/internet - http://tizen.org/privilege/network.get - - - - http://tizen.org/feature/opengles.surfaceless_context - http://tizen.org/feature/opengles.version.2_0 - diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj index 1ab3df1b63..bab1dd4c9f 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj +++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj @@ -1,18 +1,15 @@  + Exe manual $(AvsCurrentIOSTargetFramework) $(AvsMinSupportedIOSVersion) + - - - - Info.plist - + + - - true - - \ No newline at end of file + + diff --git a/samples/ControlCatalog.iOS/Info.iOS.plist b/samples/ControlCatalog.iOS/Info.plist similarity index 100% rename from samples/ControlCatalog.iOS/Info.iOS.plist rename to samples/ControlCatalog.iOS/Info.plist diff --git a/samples/ControlCatalog.iOS/ControlCatalog.tvOS.csproj b/samples/ControlCatalog.tvOS/ControlCatalog.tvOS.csproj similarity index 70% rename from samples/ControlCatalog.iOS/ControlCatalog.tvOS.csproj rename to samples/ControlCatalog.tvOS/ControlCatalog.tvOS.csproj index 35e964abab..5e191a59dd 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.tvOS.csproj +++ b/samples/ControlCatalog.tvOS/ControlCatalog.tvOS.csproj @@ -1,4 +1,5 @@ + Exe manual @@ -7,15 +8,17 @@ tvossimulator-x64 + true + - - - - Info.plist - + + - - true - + + + + + + diff --git a/samples/ControlCatalog.tvOS/Entitlements.plist b/samples/ControlCatalog.tvOS/Entitlements.plist new file mode 100644 index 0000000000..0c67376eba --- /dev/null +++ b/samples/ControlCatalog.tvOS/Entitlements.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/samples/ControlCatalog.iOS/Info.tvOS.plist b/samples/ControlCatalog.tvOS/Info.plist similarity index 100% rename from samples/ControlCatalog.iOS/Info.tvOS.plist rename to samples/ControlCatalog.tvOS/Info.plist diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index cff0bb4e92..81caa6b2e4 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,6 +1,6 @@  - netstandard2.0;$(AvsCurrentTargetFramework) + $(AvsCurrentTargetFramework) true enable true @@ -9,9 +9,9 @@ %(Filename) - + Designer - + diff --git a/samples/ControlCatalog/DecoratedWindow.xaml.cs b/samples/ControlCatalog/DecoratedWindow.xaml.cs index c97b157040..bb87d982ba 100644 --- a/samples/ControlCatalog/DecoratedWindow.xaml.cs +++ b/samples/ControlCatalog/DecoratedWindow.xaml.cs @@ -1,52 +1,45 @@ -using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; -using System; using Avalonia.Input; namespace ControlCatalog { - public class DecoratedWindow : Window + public partial class DecoratedWindow : Window { public DecoratedWindow() { - this.InitializeComponent(); - } - - void SetupSide(string name, StandardCursorType cursor, WindowEdge edge) - { - var ctl = this.Get(name); - ctl.Cursor = new Cursor(cursor); - ctl.PointerPressed += (i, e) => - { - BeginResizeDrag(edge, e); - }; - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - this.Get("TitleBar").PointerPressed += (i, e) => + InitializeComponent(); + TitleBar.PointerPressed += (i, e) => { BeginMoveDrag(e); }; - SetupSide("Left", StandardCursorType.LeftSide, WindowEdge.West); - SetupSide("Right", StandardCursorType.RightSide, WindowEdge.East); - SetupSide("Top", StandardCursorType.TopSide, WindowEdge.North); - SetupSide("Bottom", StandardCursorType.BottomSide, WindowEdge.South); - SetupSide("TopLeft", StandardCursorType.TopLeftCorner, WindowEdge.NorthWest); - SetupSide("TopRight", StandardCursorType.TopRightCorner, WindowEdge.NorthEast); - SetupSide("BottomLeft", StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest); - SetupSide("BottomRight", StandardCursorType.BottomRightCorner, WindowEdge.SouthEast); - this.Get + + Disabled + + @@ -141,7 +145,7 @@ Padding="15"> - + diff --git a/samples/ControlCatalog/Pages/ButtonsPage.xaml.cs b/samples/ControlCatalog/Pages/ButtonsPage.xaml.cs index 2d63f1fee9..6dcee1ab7d 100644 --- a/samples/ControlCatalog/Pages/ButtonsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ButtonsPage.xaml.cs @@ -1,29 +1,21 @@ using Avalonia.Controls; -using Avalonia.Markup.Xaml; +using Avalonia.Interactivity; namespace ControlCatalog.Pages { - public class ButtonsPage : UserControl + public partial class ButtonsPage : UserControl { private int repeatButtonClickCount = 0; public ButtonsPage() { InitializeComponent(); - - this.Get("RepeatButton").Click += OnRepeatButtonClick; - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); } - public void OnRepeatButtonClick(object? sender, object args) + public void OnRepeatButtonClick(object? sender, RoutedEventArgs args) { repeatButtonClickCount++; - var textBlock = this.Get("RepeatButtonTextBlock"); - textBlock.Text = $"Repeat Button: {repeatButtonClickCount}"; + RepeatButtonTextBlock.Text = $"Repeat Button: {repeatButtonClickCount}"; } } } diff --git a/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs b/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs index 2cd6e547f6..73e1fe4bba 100644 --- a/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs @@ -1,36 +1,23 @@ -using Avalonia.Controls; -using Avalonia.Markup.Xaml; using System; +using Avalonia.Controls; namespace ControlCatalog.Pages { - public class CalendarDatePickerPage : UserControl + public partial class CalendarDatePickerPage : UserControl { public CalendarDatePickerPage() { InitializeComponent(); - - var dp1 = this.Get("DatePicker1"); - var dp2 = this.Get("DatePicker2"); - var dp3 = this.Get("DatePicker3"); - var dp4 = this.Get("DatePicker4"); - var dp5 = this.Get("DatePicker5"); - dp1.SelectedDate = DateTime.Today; - dp2.SelectedDate = DateTime.Today.AddDays(10); - dp3.SelectedDate = DateTime.Today.AddDays(20); - dp5.SelectedDate = DateTime.Today; + DatePicker1.SelectedDate = DateTime.Today; + DatePicker2.SelectedDate = DateTime.Today.AddDays(10); + DatePicker3.SelectedDate = DateTime.Today.AddDays(20); + DatePicker5.SelectedDate = DateTime.Today; - dp4.TemplateApplied += (s, e) => + DatePicker4.TemplateApplied += (s, e) => { - dp4.BlackoutDates?.AddDatesInPast(); + DatePicker4.BlackoutDates?.AddDatesInPast(); }; - - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); } } } diff --git a/samples/ControlCatalog/Pages/CalendarPage.xaml.cs b/samples/ControlCatalog/Pages/CalendarPage.xaml.cs index e0f4268016..56f511be55 100644 --- a/samples/ControlCatalog/Pages/CalendarPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CalendarPage.xaml.cs @@ -1,28 +1,20 @@ -using Avalonia.Controls; -using Avalonia.Markup.Xaml; using System; +using Avalonia.Controls; namespace ControlCatalog.Pages { - public class CalendarPage : UserControl + public partial class CalendarPage : UserControl { public CalendarPage() { - this.InitializeComponent(); + InitializeComponent(); - var today = DateTime.Today; - var cal1 = this.Get("DisplayDatesCalendar"); - cal1.DisplayDateStart = today.AddDays(-25); - cal1.DisplayDateEnd = today.AddDays(25); + var today = DateTime.Today; + DisplayDatesCalendar.DisplayDateStart = today.AddDays(-25); + DisplayDatesCalendar.DisplayDateEnd = today.AddDays(25); - var cal2 = this.Get("BlackoutDatesCalendar"); - cal2.BlackoutDates.AddDatesInPast(); - cal2.BlackoutDates.Add(new CalendarDateRange(today.AddDays(6))); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); + BlackoutDatesCalendar.BlackoutDates.AddDatesInPast(); + BlackoutDatesCalendar.BlackoutDates.Add(new CalendarDateRange(today.AddDays(6))); } } } diff --git a/samples/ControlCatalog/Pages/CanvasPage.xaml.cs b/samples/ControlCatalog/Pages/CanvasPage.xaml.cs index f43d974c18..e52df597a9 100644 --- a/samples/ControlCatalog/Pages/CanvasPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CanvasPage.xaml.cs @@ -1,18 +1,12 @@ using Avalonia.Controls; -using Avalonia.Markup.Xaml; namespace ControlCatalog.Pages { - public class CanvasPage : UserControl + public partial class CanvasPage : UserControl { public CanvasPage() { - this.InitializeComponent(); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); + InitializeComponent(); } } } diff --git a/samples/ControlCatalog/Pages/CarouselPage.xaml.cs b/samples/ControlCatalog/Pages/CarouselPage.xaml.cs index c6aab5c4d5..713da34051 100644 --- a/samples/ControlCatalog/Pages/CarouselPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CarouselPage.xaml.cs @@ -1,53 +1,35 @@ using System; using Avalonia.Animation; using Avalonia.Controls; -using Avalonia.Markup.Xaml; namespace ControlCatalog.Pages { - public class CarouselPage : UserControl + public partial class CarouselPage : UserControl { - private Carousel _carousel; - private Button _left; - private Button _right; - private ComboBox _transition; - private ComboBox _orientation; - public CarouselPage() { - this.InitializeComponent(); - _carousel = this.Get("carousel"); - _left = this.Get - - - + diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index 5b7814fb5d..0e2ad7d2b9 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -21,22 +21,24 @@ using Avalonia.Platform.Storage.FileIO; namespace ControlCatalog.Pages { - public class DialogsPage : UserControl + public partial class DialogsPage : UserControl { public DialogsPage() { - this.InitializeComponent(); + InitializeComponent(); IStorageFolder? lastSelectedDirectory = null; IStorageItem? lastSelectedItem = null; bool ignoreTextChanged = false; - var results = this.Get("PickerLastResults"); - var resultsVisible = this.Get("PickerLastResultsVisible"); - var bookmarkContainer = this.Get("BookmarkContainer"); - var openedFileContent = this.Get("OpenedFileContent"); - var openMultiple = this.Get("OpenMultiple"); - var currentFolderBox = this.Get("CurrentFolderBox"); + var results = PickerLastResults; + var resultsVisible = PickerLastResultsVisible; + var bookmarkContainer = BookmarkContainer; + var openedFileContent = OpenedFileContent; + var openMultiple = OpenMultiple; + var currentFolderBox = CurrentFolderBox; + var useSuggestedFilter = UseSuggestedFilter; + var suggestedFilterSelector = SuggestedFilterSelector; currentFolderBox.TextChanged += async (sender, args) => { @@ -61,7 +63,7 @@ namespace ControlCatalog.Pages } catch (SecurityException) { - + } } } @@ -76,9 +78,9 @@ namespace ControlCatalog.Pages }).ToList() ?? new List(); } - List? GetFileTypes() + List? BuildFileTypes() { - var selectedItem = (this.Get("FilterSelector").SelectedItem as ComboBoxItem)?.Content + var selectedItem = (FilterSelector.SelectedItem as ComboBoxItem)?.Content ?? "None"; var binLogType = new FilePickerFileType("Binary Log") @@ -115,7 +117,65 @@ namespace ControlCatalog.Pages }; } - this.Get"; - var mfxt = this.Get("MenuFlyoutXamlText"); + var mfxt = this.MenuFlyoutXamlText; mfxt.Text = ""; - var afxt = this.Get("AttachedFlyoutXamlText"); + var afxt = this.AttachedFlyoutXamlText; afxt.Text = "\n" + " \n" + " \n" + @@ -63,7 +59,7 @@ namespace ControlCatalog.Pages "\n\n In DoubleTapped handler:\n" + "FlyoutBase.ShowAttachedFlyout(AttachedFlyoutPanel);"; - var sfxt = this.Get("SharedFlyoutXamlText"); + var sfxt = this.SharedFlyoutXamlText; sfxt.Text = "Declare a flyout in Resources:\n" + "\n" + " \n" + diff --git a/samples/ControlCatalog/Pages/GesturePage.cs b/samples/ControlCatalog/Pages/GesturePage.cs index 9164384eae..c480b512b4 100644 --- a/samples/ControlCatalog/Pages/GesturePage.cs +++ b/samples/ControlCatalog/Pages/GesturePage.cs @@ -1,75 +1,61 @@ using System; -using System.Numerics; using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.LogicalTree; -using Avalonia.Markup.Xaml; using Avalonia.Rendering.Composition; using Avalonia.Utilities; namespace ControlCatalog.Pages { - public class GesturePage : UserControl + public partial class GesturePage : UserControl { private bool _isInit; private double _currentScale; public GesturePage() { - this.InitializeComponent(); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); + InitializeComponent(); } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - if(_isInit) + if (_isInit) { return; } _isInit = true; - SetPullHandlers(this.Find("TopPullZone"), false); - SetPullHandlers(this.Find("BottomPullZone"), true); - SetPullHandlers(this.Find("RightPullZone"), true); - SetPullHandlers(this.Find("LeftPullZone"), false); + SetPullHandlers(TopPullZone, false); + SetPullHandlers(BottomPullZone, true); + SetPullHandlers(RightPullZone, true); + SetPullHandlers(LeftPullZone, false); - var image = this.Get("PinchImage"); + var image = PinchImage; SetPinchHandlers(image); - var reset = this.Get + Drop Position: + + Status: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/IntegrationTestApp/Pages/DragDropPage.axaml.cs b/samples/IntegrationTestApp/Pages/DragDropPage.axaml.cs new file mode 100644 index 0000000000..ba5ad0a3b5 --- /dev/null +++ b/samples/IntegrationTestApp/Pages/DragDropPage.axaml.cs @@ -0,0 +1,103 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Input.Platform; +using Avalonia.Interactivity; + +namespace IntegrationTestApp.Pages; + +public partial class DragDropPage : UserControl +{ + public DragDropPage() + { + InitializeComponent(); + + // Set up drag-drop event handlers + AddHandler(DragDrop.DragOverEvent, DropTarget_DragOver); + AddHandler(DragDrop.DropEvent, DropTarget_Drop); + } + + private async void DragSource_PointerPressed(object? sender, PointerPressedEventArgs e) + { + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + { + var dragData = new DataTransfer(); + dragData.Add(DataTransferItem.CreateText("TestDragData")); + + DragDropStatus.Text = "Dragging..."; + + var result = await DragDrop.DoDragDropAsync(e, dragData, DragDropEffects.Copy | DragDropEffects.Move); + + DragDropStatus.Text = result switch + { + DragDropEffects.Copy => "Copied", + DragDropEffects.Move => "Moved", + DragDropEffects.None => "Cancelled", + _ => $"Result: {result}" + }; + } + } + + private void DropTarget_DragOver(object? sender, DragEventArgs e) + { + // Only handle events for the drop target + if (e.Source != DropTarget && !IsChildOf(e.Source as Visual, DropTarget)) + return; + + e.DragEffects = DragDropEffects.Copy; + + // Get the position relative to the drop target + var position = e.GetPosition(DropTarget); + DropPosition.Text = $"DragOver: ({position.X:F0}, {position.Y:F0})"; + } + + private void DropTarget_Drop(object? sender, DragEventArgs e) + { + // Only handle events for the drop target + if (e.Source != DropTarget && !IsChildOf(e.Source as Visual, DropTarget)) + return; + + // Get the position relative to the drop target + var position = e.GetPosition(DropTarget); + DropPosition.Text = $"Drop: ({position.X:F0}, {position.Y:F0})"; + + // Check if the position is within reasonable bounds of the drop target + var bounds = DropTarget.Bounds; + var isWithinBounds = position.X >= 0 && position.X <= bounds.Width && + position.Y >= 0 && position.Y <= bounds.Height; + + var text = e.DataTransfer.TryGetText(); + if (text != null) + { + DropTargetText.Text = isWithinBounds + ? $"Dropped: {text} at ({position.X:F0}, {position.Y:F0})" + : $"ERROR: Position out of bounds! ({position.X:F0}, {position.Y:F0})"; + DragDropStatus.Text = isWithinBounds ? "Drop OK" : "Drop position ERROR"; + } + + e.DragEffects = DragDropEffects.Copy; + } + + private static bool IsChildOf(Visual? child, Visual? parent) + { + if (child == null || parent == null) + return false; + + var current = child.Parent as Visual; + while (current != null) + { + if (current == parent) + return true; + current = current.Parent as Visual; + } + return false; + } + + private void ResetDragDrop_Click(object? sender, RoutedEventArgs e) + { + DropPosition.Text = string.Empty; + DragDropStatus.Text = string.Empty; + DropTargetText.Text = "Drop items here"; + } +} diff --git a/samples/IntegrationTestApp/Pages/WindowPage.axaml.cs b/samples/IntegrationTestApp/Pages/WindowPage.axaml.cs index b7f505a7b2..baae2b8766 100644 --- a/samples/IntegrationTestApp/Pages/WindowPage.axaml.cs +++ b/samples/IntegrationTestApp/Pages/WindowPage.axaml.cs @@ -136,6 +136,7 @@ public partial class WindowPage : UserControl Width = 200, Height = 200, Background = Brushes.Green, + SystemDecorations = SystemDecorations.None, WindowStartupLocation = WindowStartupLocation.CenterOwner, Content = new Border { @@ -145,7 +146,7 @@ public partial class WindowPage : UserControl } }; - backgroundWindow.PointerPressed += (_, _) => backgroundWindow.Close(); + popup.PointerPressed += (_, _) => backgroundWindow.Close(); backgroundWindow.Show(Window); popup.Open(); diff --git a/samples/IntegrationTestApp/ShowWindowTest.axaml b/samples/IntegrationTestApp/ShowWindowTest.axaml index c43e00497f..272c61ed0c 100644 --- a/samples/IntegrationTestApp/ShowWindowTest.axaml +++ b/samples/IntegrationTestApp/ShowWindowTest.axaml @@ -15,7 +15,7 @@ - + @@ -62,13 +62,19 @@