diff --git a/.editorconfig b/.editorconfig index 30edee1633..9ae52b8bbd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -134,9 +134,26 @@ csharp_space_between_parentheses = false csharp_space_between_square_brackets = false space_within_single_line_array_initializer_braces = true +#Net Analyzer +dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomment when all violations are fixed. + +# CA1802: Use literals where appropriate +dotnet_diagnostic.CA1802.severity = warning +# CA1820: Test for empty strings using string length +dotnet_diagnostic.CA1820.severity = warning +# CA1821: Remove empty finalizers +dotnet_diagnostic.CA1821.severity = warning +# CA1825: Avoid zero-length array allocations +dotnet_diagnostic.CA1825.severity = warning +#CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters +dotnet_diagnostic.CA1847.severity = warning + # Wrapping preferences csharp_wrap_before_ternary_opsigns = false +# Avalonia DevAnalyzer preferences +dotnet_diagnostic.AVADEV2001.severity = error + # Xaml files [*.{xaml,axaml}] indent_size = 2 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index c5a719ce90..df070c35cf 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ +github: avaloniaui open_collective: avalonia diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 46e8665945..2f63750cdc 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -21,7 +21,7 @@ - [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Documentation with user documentation ## Breaking changes - + ## Obsoletions / Deprecations diff --git a/.gitignore b/.gitignore index 44fe5e4ba4..61a3b53de1 100644 --- a/.gitignore +++ b/.gitignore @@ -212,3 +212,7 @@ coc-settings.json *.map src/Web/Avalonia.Web.Blazor/wwwroot/*.js src/Web/Avalonia.Web.Blazor/Interop/Typescript/*.js +node_modules +src/Web/Avalonia.Web.Blazor/webapp/package-lock.json +src/Web/Avalonia.Web.Blazor/wwwroot +src/Web/Avalonia.Web/wwwroot diff --git a/.gitmodules b/.gitmodules index 2d11fdfa9e..032bc879cc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github"] path = src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github url = https://github.com/kekekeks/XamlX.git +[submodule "nukebuild/il-repack"] + path = nukebuild/il-repack + url = https://github.com/Gillibald/il-repack diff --git a/.ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject b/.ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Web.v3.ncrunchproject b/.ncrunch/Avalonia.Web.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.Web.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject index 28b692bb51..95a483b433 100644 --- a/.ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Win32.net6.0.v3.ncrunchproject @@ -1,7 +1,3 @@  - - - ..\..\tools\MicroComGenerator\bin\Debug\net6.0\**.* - - + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject index 28b692bb51..95a483b433 100644 --- a/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject +++ b/.ncrunch/Avalonia.Win32.netstandard2.0.v3.ncrunchproject @@ -1,7 +1,3 @@  - - - ..\..\tools\MicroComGenerator\bin\Debug\net6.0\**.* - - + \ No newline at end of file diff --git a/.ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject b/.ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.Android.v3.ncrunchproject b/.ncrunch/MobileSandbox.Android.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.Android.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.Desktop.v3.ncrunchproject b/.ncrunch/MobileSandbox.Desktop.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.Desktop.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.iOS.v3.ncrunchproject b/.ncrunch/MobileSandbox.iOS.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.iOS.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.net6.0.v3.ncrunchproject b/.ncrunch/MobileSandbox.net6.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.net6.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.netstandard2.0.v3.ncrunchproject b/.ncrunch/MobileSandbox.netstandard2.0.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.netstandard2.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/_build.v3.ncrunchproject b/.ncrunch/_build.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/_build.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.nuke b/.nuke deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json new file mode 100644 index 0000000000..5bbc3d6915 --- /dev/null +++ b/.nuke/build.schema.json @@ -0,0 +1,148 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Build Schema", + "$ref": "#/definitions/build", + "definitions": { + "build": { + "type": "object", + "properties": { + "Configuration": { + "type": "string", + "description": "configuration" + }, + "Continue": { + "type": "boolean", + "description": "Indicates to continue a previously failed build attempt" + }, + "ForceNugetVersion": { + "type": "string", + "description": "force-nuget-version" + }, + "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" + ] + }, + "NoLogo": { + "type": "boolean", + "description": "Disables displaying the NUKE logo" + }, + "Partition": { + "type": "string", + "description": "Partition to use on CI" + }, + "Plan": { + "type": "boolean", + "description": "Shows the execution plan (HTML)" + }, + "Profile": { + "type": "array", + "description": "Defines the profiles to load", + "items": { + "type": "string" + } + }, + "Root": { + "type": "string", + "description": "Root directory during build execution" + }, + "Skip": { + "type": "array", + "description": "List of targets to be skipped. Empty list skips all dependencies", + "items": { + "type": "string", + "enum": [ + "CiAzureLinux", + "CiAzureOSX", + "CiAzureWindows", + "Clean", + "Compile", + "CompileHtmlPreviewer", + "CompileNative", + "CreateIntermediateNugetPackages", + "CreateNugetPackages", + "GenerateCppHeaders", + "Package", + "RunCoreLibsTests", + "RunDesignerTests", + "RunHtmlPreviewerTests", + "RunLeakTests", + "RunRenderTests", + "RunTests", + "ZipFiles" + ] + } + }, + "SkipPreviewer": { + "type": "boolean", + "description": "skip-previewer" + }, + "SkipTests": { + "type": "boolean", + "description": "skip-tests" + }, + "Solution": { + "type": "string", + "description": "Path to a solution file that is automatically loaded. Default is Avalonia.sln" + }, + "Target": { + "type": "array", + "description": "List of targets to be invoked. Default is '{default_target}'", + "items": { + "type": "string", + "enum": [ + "CiAzureLinux", + "CiAzureOSX", + "CiAzureWindows", + "Clean", + "Compile", + "CompileHtmlPreviewer", + "CompileNative", + "CreateIntermediateNugetPackages", + "CreateNugetPackages", + "GenerateCppHeaders", + "Package", + "RunCoreLibsTests", + "RunDesignerTests", + "RunHtmlPreviewerTests", + "RunLeakTests", + "RunRenderTests", + "RunTests", + "ZipFiles" + ] + } + }, + "Verbosity": { + "type": "string", + "description": "Logging verbosity during build execution. Default is 'Normal'", + "enum": [ + "Minimal", + "Normal", + "Quiet", + "Verbose" + ] + } + } + } + } +} \ No newline at end of file diff --git a/.nuke/parameters.json b/.nuke/parameters.json new file mode 100644 index 0000000000..42521bb7dd --- /dev/null +++ b/.nuke/parameters.json @@ -0,0 +1,4 @@ +{ + "$schema": "./build.schema.json", + "Solution": "" +} \ No newline at end of file diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf new file mode 100644 index 0000000000..6ba05332be --- /dev/null +++ b/Avalonia.Desktop.slnf @@ -0,0 +1,60 @@ +{ + "solution": { + "path": "Avalonia.sln", + "projects": [ + "packages\\Avalonia\\Avalonia.csproj", + "samples\\ControlCatalog.NetCore\\ControlCatalog.NetCore.csproj", + "samples\\ControlCatalog\\ControlCatalog.csproj", + "samples\\IntegrationTestApp\\IntegrationTestApp.csproj", + "samples\\MiniMvvm\\MiniMvvm.csproj", + "samples\\SampleControls\\ControlSamples.csproj", + "samples\\Sandbox\\Sandbox.csproj", + "src\\Avalonia.Base\\Avalonia.Base.csproj", + "src\\Avalonia.Build.Tasks\\Avalonia.Build.Tasks.csproj", + "src\\Avalonia.Controls.ColorPicker\\Avalonia.Controls.ColorPicker.csproj", + "src\\Avalonia.Controls.DataGrid\\Avalonia.Controls.DataGrid.csproj", + "src\\Avalonia.Controls\\Avalonia.Controls.csproj", + "src\\Avalonia.DesignerSupport\\Avalonia.DesignerSupport.csproj", + "src\\Avalonia.Desktop\\Avalonia.Desktop.csproj", + "src\\Avalonia.Diagnostics\\Avalonia.Diagnostics.csproj", + "src\\Avalonia.Dialogs\\Avalonia.Dialogs.csproj", + "src\\Avalonia.FreeDesktop\\Avalonia.FreeDesktop.csproj", + "src\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj", + "src\\Avalonia.Headless\\Avalonia.Headless.csproj", + "src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj", + "src\\Avalonia.Native\\Avalonia.Native.csproj", + "src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj", + "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", + "src\\Avalonia.X11\\Avalonia.X11.csproj", + "src\\Linux\\Avalonia.LinuxFramebuffer\\Avalonia.LinuxFramebuffer.csproj", + "src\\Markup\\Avalonia.Markup.Xaml.Loader\\Avalonia.Markup.Xaml.Loader.csproj", + "src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj", + "src\\Markup\\Avalonia.Markup\\Avalonia.Markup.csproj", + "src\\Skia\\Avalonia.Skia\\Avalonia.Skia.csproj", + "src\\Windows\\Avalonia.Direct2D1\\Avalonia.Direct2D1.csproj", + "src\\Windows\\Avalonia.Win32.Interop\\Avalonia.Win32.Interop.csproj", + "src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj", + "src\\tools\\DevAnalyzers\\DevAnalyzers.csproj", + "src\\tools\\DevGenerators\\DevGenerators.csproj", + "tests\\Avalonia.Base.UnitTests\\Avalonia.Base.UnitTests.csproj", + "tests\\Avalonia.Benchmarks\\Avalonia.Benchmarks.csproj", + "tests\\Avalonia.Controls.DataGrid.UnitTests\\Avalonia.Controls.DataGrid.UnitTests.csproj", + "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.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.Skia.RenderTests\\Avalonia.Skia.RenderTests.csproj", + "tests\\Avalonia.Skia.UnitTests\\Avalonia.Skia.UnitTests.csproj", + "tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj" + ] + } +} \ No newline at end of file diff --git a/Avalonia.sln b/Avalonia.sln index 4999719676..e2f04ddc35 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -13,7 +13,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1", "src\W 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.Default", "src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Simple", "src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Diagnostics", "src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj", "{7062AE20-5DCC-4442-9645-8195BDECE63E}" EndProject @@ -41,6 +41,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DE src\Shared\IsExternalInit.cs = src\Shared\IsExternalInit.cs src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs + src\Avalonia.Base\Compatibility\StringCompatibilityExtensions.cs = src\Avalonia.Base\Compatibility\StringCompatibilityExtensions.cs EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}" @@ -90,6 +91,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}" ProjectSection(SolutionItems) = preProject build\ApiDiff.props = build\ApiDiff.props + build\AvaloniaPublicKey.props = build\AvaloniaPublicKey.props build\Base.props = build\Base.props build\Binding.props = build\Binding.props build\CoreLibraries.props = build\CoreLibraries.props @@ -102,8 +104,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\Microsoft.CSharp.props = build\Microsoft.CSharp.props build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props build\Moq.props = build\Moq.props + build\NetAnalyzers.props = build\NetAnalyzers.props 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 @@ -198,8 +202,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{86A3F706-DC3 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web.Blazor", "src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj", "{25831348-EB2A-483E-9576-E8F6528674A5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Web", "samples\ControlCatalog.Web\ControlCatalog.Web.csproj", "{C08E9894-AA92-426E-BF56-033E262CAD3E}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsInteropTest", "samples\interop\WindowsInteropTest\WindowsInteropTest.csproj", "{26A98DA1-D89D-4A95-8152-349F404DA2E2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\SampleControls\ControlSamples.csproj", "{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}" @@ -212,7 +214,21 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ColorPick EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{EABE2161-989B-42BF-BD8D-1E34B20C21F1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevGenerators", "src\tools\DevGenerators\DevGenerators.csproj", "{1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevGenerators", "src\tools\DevGenerators\DevGenerators.csproj", "{1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web", "src\Web\Avalonia.Web\Avalonia.Web.csproj", "{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox", "samples\MobileSandbox\MobileSandbox.csproj", "{3B8519C1-2F51-4F12-A348-120AB91D4532}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Android", "samples\MobileSandbox.Android\MobileSandbox.Android.csproj", "{C90FE60B-B01E-4F35-91D6-379D6966030F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.iOS", "samples\MobileSandbox.iOS\MobileSandbox.iOS.csproj", "{FED9A71D-00D7-4F40-A9E4-1229EEA28EEB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Desktop", "samples\MobileSandbox.Desktop\MobileSandbox.Desktop.csproj", "{62D392C9-81CF-487F-92E8-598B2AF3FDCE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Blazor.Web", "samples\ControlCatalog.Blazor.Web\ControlCatalog.Blazor.Web.csproj", "{6A710364-AE6D-40BD-968B-024311527AC2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Web", "samples\ControlCatalog.Web\ControlCatalog.Web.csproj", "{8B3E8405-DE18-4048-A459-9CA4AC3319A2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -399,9 +415,7 @@ Global {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|Any CPU.Build.0 = Release|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.Build.0 = Debug|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.Build.0 = Release|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Debug|Any CPU.Build.0 = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -470,10 +484,6 @@ Global {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|Any CPU.Build.0 = Debug|Any CPU {25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.Build.0 = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|Any CPU.Build.0 = Release|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.Build.0 = Debug|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -502,6 +512,35 @@ Global {1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}.Debug|Any CPU.Build.0 = Debug|Any CPU {1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}.Release|Any CPU.ActiveCfg = Release|Any CPU {1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}.Release|Any CPU.Build.0 = Release|Any CPU + {76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Release|Any CPU.Build.0 = Release|Any CPU + {3B8519C1-2F51-4F12-A348-120AB91D4532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B8519C1-2F51-4F12-A348-120AB91D4532}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B8519C1-2F51-4F12-A348-120AB91D4532}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B8519C1-2F51-4F12-A348-120AB91D4532}.Release|Any CPU.Build.0 = Release|Any CPU + {C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {C90FE60B-B01E-4F35-91D6-379D6966030F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C90FE60B-B01E-4F35-91D6-379D6966030F}.Release|Any CPU.Build.0 = Release|Any CPU + {FED9A71D-00D7-4F40-A9E4-1229EEA28EEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FED9A71D-00D7-4F40-A9E4-1229EEA28EEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FED9A71D-00D7-4F40-A9E4-1229EEA28EEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FED9A71D-00D7-4F40-A9E4-1229EEA28EEB}.Release|Any CPU.Build.0 = Release|Any CPU + {62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Release|Any CPU.Build.0 = Release|Any CPU + {6A710364-AE6D-40BD-968B-024311527AC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A710364-AE6D-40BD-968B-024311527AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A710364-AE6D-40BD-968B-024311527AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A710364-AE6D-40BD-968B-024311527AC2}.Release|Any CPU.Build.0 = Release|Any CPU + {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -545,6 +584,7 @@ Global {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} {351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C} {11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098} @@ -552,14 +592,19 @@ Global {676D6BFD-029D-4E43-BFC7-3892265CE251} = {9B9E3891-2366-4253-A952-D08BCEB71098} {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {25831348-EB2A-483E-9576-E8F6528674A5} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268} - {C08E9894-AA92-426E-BF56-033E262CAD3E} = {9B9E3891-2366-4253-A952-D08BCEB71098} {26A98DA1-D89D-4A95-8152-349F404DA2E2} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270} = {9B9E3891-2366-4253-A952-D08BCEB71098} {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {EABE2161-989B-42BF-BD8D-1E34B20C21F1} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} + {76D39FF6-6B4F-46C4-93CD-E6FC4665739E} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268} + {3B8519C1-2F51-4F12-A348-120AB91D4532} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {C90FE60B-B01E-4F35-91D6-379D6966030F} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {FED9A71D-00D7-4F40-A9E4-1229EEA28EEB} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {62D392C9-81CF-487F-92E8-598B2AF3FDCE} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {6A710364-AE6D-40BD-968B-024311527AC2} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {8B3E8405-DE18-4048-A459-9CA4AC3319A2} = {9B9E3891-2366-4253-A952-D08BCEB71098} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000000..73954c7f4d --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,5 @@ + + + $(DefineConstants);NET7SDK + + diff --git a/Documentation/build.md b/Documentation/build.md index ddd38be887..fd6b26337c 100644 --- a/Documentation/build.md +++ b/Documentation/build.md @@ -1,8 +1,8 @@ # Windows -Avalonia requires at least Visual Studio 2022 and dotnet 6 SDK 6.0.100 to build on all platforms. +Avalonia requires at least Visual Studio 2022 and dotnet 7-rc2 SDK 7.0.100-rc.2 to build on all platforms. -### Clone the Avalonia repository +## Clone the Avalonia repository ``` git clone https://github.com/AvaloniaUI/Avalonia.git @@ -10,15 +10,30 @@ cd Avalonia git submodule update --init ``` -### Install the required version of the .NET Core SDK +## Install the required version of the .NET Core SDK Go to https://dotnet.microsoft.com/download/visual-studio-sdks and install the latest version of the .NET Core SDK compatible with Avalonia UI. Make sure to download the SDK (not just the "runtime") package. The version compatible is indicated within the [global.json](https://github.com/AvaloniaUI/Avalonia/blob/master/global.json) file. Note that Avalonia UI does not always use the latest version and is hardcoded to use the last version known to be compatible (SDK releases may break the builds from time-to-time). -### Open in Visual Studio +## Build and Run Avalonia -Open the `Avalonia.sln` solution in Visual Studio 2022 or newer. The free Visual Studio Community edition works fine. Build and run the `Samples\ControlCatalog.Desktop` or `ControlCatalog.NetCore` project to see the sample application. +``` +cd samples\ControlCatalog.NetCore +dotnet restore +dotnet run +``` + +## Opening in Visual Studio -### Troubleshooting +If you want to open Avalonia in Visual Studio you have two options: + +- Avalonia.sln: This contains the whole of Avalonia in including desktop, mobile and web. You must have a number of dotnet workloads installed in order to build everything in this solution +- Avalonia.Desktop.slnf: This solution filter opens only the parts of Avalonia required to run on desktop. This requires no extra workloads to be installed. + +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. + +### Visual Studio Troubleshooting * **Error CS0006: Avalonia.DesktopRuntime.dll could not be found** @@ -35,16 +50,15 @@ It's *not* possible to build the *whole* project on Linux/macOS. You can only bu MonoDevelop, Xamarin Studio and Visual Studio for Mac aren't capable of properly opening our solution. You can use Rider (at least 2017.2 EAP) or VSCode instead. They will fail to load most of platform specific projects, but you don't need them to run on .NET Core. -### Install the latest version of the .NET Core SDK +## Install the latest version of the .NET Core SDK Go to https://www.microsoft.com/net/core and follow the instructions for your OS. Make sure to download the SDK (not just the "runtime") package. -### Additional requirements for macOS +## Additional requirements for macOS The build process needs [Xcode](https://developer.apple.com/xcode/) to build the native library. Following the install instructions at the [Xcode](https://developer.apple.com/xcode/) website to properly install. - -### Clone the Avalonia repository +## Clone the Avalonia repository ``` git clone https://github.com/AvaloniaUI/Avalonia.git @@ -52,7 +66,7 @@ cd Avalonia git submodule update --init --recursive ``` -### Build native libraries (macOS only) +## Build native libraries (macOS only) On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/). Execute the build script in the root project with the `CompileNative` task. It will build the headers, build the libraries, and place them in the appropriate place to allow .NET to find them at compilation and run time. @@ -60,7 +74,7 @@ On macOS it is necessary to build and manually install the respective native lib ./build.sh CompileNative ``` -### Build and Run Avalonia +## Build and Run Avalonia ``` cd samples/ControlCatalog.NetCore diff --git a/NOTICE.md b/NOTICE.md index 92fd725957..e97fc654c9 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -111,7 +111,7 @@ DEALINGS IN THE SOFTWARE. # Metsys.Bson -Copyright (c) 2010, Karl Seguin - http://www.openmymind.net/ +Copyright (c) 2010, Karl Seguin - https://www.openmymind.net/ All rights reserved. Redistribution and use in source and binary forms, with or without @@ -302,4 +302,4 @@ https://github.com/chromium/chromium // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/azure-pipelines-integrationtests.yml b/azure-pipelines-integrationtests.yml index 0b79758c76..43253ac6be 100644 --- a/azure-pipelines-integrationtests.yml +++ b/azure-pipelines-integrationtests.yml @@ -12,6 +12,16 @@ jobs: name: 'AvaloniaMacPool' steps: + - task: UseDotNet@2 + displayName: 'Use .NET Core SDK 6.0.401' + inputs: + version: 6.0.401 + + - task: UseDotNet@2 + displayName: 'Use .NET Core SDK 7.0.100' + inputs: + version: 7.0.100 + - script: system_profiler SPDisplaysDataType |grep Resolution - script: | @@ -22,7 +32,7 @@ jobs: 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/net6.0/osx-arm64/publish/IntegrationTestApp.app + open -n ./samples/IntegrationTestApp/bin/Debug/net7.0/osx-arm64/publish/IntegrationTestApp.app pkill IntegrationTestApp - task: DotNetCoreCLI@2 @@ -41,9 +51,14 @@ jobs: steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.202' + displayName: 'Use .NET Core SDK 6.0.401' + inputs: + version: 6.0.401 + + - task: UseDotNet@2 + displayName: 'Use .NET Core SDK 7.0.100' inputs: - version: 6.0.202 + version: 7.0.100 - task: Windows Application Driver@0 inputs: @@ -57,6 +72,7 @@ jobs: projects: 'samples/IntegrationTestApp/IntegrationTestApp.csproj' - task: DotNetCoreCLI@2 + retryCountOnTaskFailure: 3 inputs: command: 'test' projects: 'tests/Avalonia.IntegrationTests.Appium/Avalonia.IntegrationTests.Appium.csproj' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index edf3c3d819..a3bbc33418 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,7 +6,6 @@ jobs: variables: SolutionDir: '$(Build.SourcesDirectory)' steps: - - task: PowerShell@2 displayName: Get PR Number inputs: @@ -31,14 +30,20 @@ jobs: vmImage: 'ubuntu-20.04' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.1.418' + displayName: 'Use .NET Core SDK 6.0.401' inputs: - version: 3.1.418 + version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.202' + displayName: 'Use .NET Core SDK 7.0' + inputs: + version: 7.0.100 + + - task: CmdLine@2 + displayName: 'Install Workloads' inputs: - version: 6.0.202 + script: | + dotnet workload install wasm-tools wasm-experimental - task: CmdLine@2 displayName: 'Run Build' @@ -59,25 +64,24 @@ jobs: variables: SolutionDir: '$(Build.SourcesDirectory)' pool: - vmImage: 'macOS-10.15' + vmImage: 'macos-12' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.1.418' + displayName: 'Use .NET Core SDK 6.0.401' inputs: - version: 3.1.418 + version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.202' + displayName: 'Use .NET Core SDK 7.0.100' inputs: - version: 6.0.202 - + version: 7.0.100 + - task: CmdLine@2 - displayName: 'Install Mono 5.18' + displayName: 'Install Workloads' inputs: script: | - curl -o ./mono.pkg https://download.mono-project.com/archive/5.18.0/macos-10-universal/MonoFramework-MDK-5.18.0.225.macos10.xamarin.universal.pkg - sudo installer -verbose -pkg ./mono.pkg -target / - + dotnet workload install wasm-tools wasm-experimental + - task: CmdLine@2 displayName: 'Generate avalonia-native' inputs: @@ -91,10 +95,10 @@ jobs: inputs: actions: 'build' scheme: '' - sdk: 'macosx11.1' + sdk: 'macosx12.3' configuration: 'Release' xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace' - xcodeVersion: '12' # Options: 8, 9, default, specifyPath + xcodeVersion: '13' # Options: 8, 9, default, specifyPath args: '-derivedDataPath ./' - task: CmdLine@2 @@ -134,26 +138,26 @@ jobs: SolutionDir: '$(Build.SourcesDirectory)' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.1.418' + displayName: 'Use .NET Core SDK 6.0.401' inputs: - version: 3.1.418 + version: 6.0.401 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.202' + displayName: 'Use .NET Core SDK 7.0.100' inputs: - version: 6.0.202 + version: 7.0.100 - task: CmdLine@2 displayName: 'Install Workloads' inputs: script: | - dotnet workload install android ios + dotnet workload install android ios wasm-tools wasm-experimental - task: CmdLine@2 displayName: 'Install Nuke' inputs: script: | - dotnet tool install --global Nuke.GlobalTool --version 0.24.0 + dotnet tool install --global Nuke.GlobalTool --version 6.2.1 - task: CmdLine@2 displayName: 'Run Nuke' diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000000..b08cc590f4 --- /dev/null +++ b/build.cmd @@ -0,0 +1,7 @@ +:; set -eo pipefail +:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) +:; ${SCRIPT_DIR}/build.sh "$@" +:; exit $? + +@ECHO OFF +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* diff --git a/build.ps1 b/build.ps1 index 985e8abcee..997e5b423f 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,13 +1,12 @@ [CmdletBinding()] Param( - #[switch]$CustomParam, [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] [string[]]$BuildArguments ) -Write-Output "Windows PowerShell $($Host.Version)" +Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" -Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { exit 1 } +Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent ########################################################################### @@ -15,15 +14,15 @@ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent ########################################################################### $BuildProjectFile = "$PSScriptRoot\nukebuild\_build.csproj" -$TempDirectory = "$PSScriptRoot\\.tmp" +$TempDirectory = "$PSScriptRoot\\.nuke\temp" $DotNetGlobalFile = "$PSScriptRoot\\global.json" -$DotNetInstallUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.ps1" +$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" $DotNetChannel = "Current" $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 -$env:NUGET_XMLDOC_MODE = "skip" +$env:DOTNET_MULTILEVEL_LOOKUP = 0 ########################################################################### # EXECUTION @@ -34,38 +33,37 @@ function ExecSafe([scriptblock] $cmd) { if ($LASTEXITCODE) { exit $LASTEXITCODE } } -# If global.json exists, load expected version -if (Test-Path $DotNetGlobalFile) { - $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) - if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { - $DotNetVersion = $DotNetGlobal.sdk.version - } -} - -# If dotnet is installed locally, and expected version is not set or installation matches the expected version +# If dotnet CLI is installed globally and it matches requested version, use for execution if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` - (!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) { + $(dotnet --version) -and $LASTEXITCODE -eq 0) { $env:DOTNET_EXE = (Get-Command "dotnet").Path } else { - $DotNetDirectory = "$TempDirectory\dotnet-win" - $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" - # Download install script $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" - mkdir -force $TempDirectory > $null + New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) + # If global.json exists, load expected version + if (Test-Path $DotNetGlobalFile) { + $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) + if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { + $DotNetVersion = $DotNetGlobal.sdk.version + } + } + # Install by channel or version + $DotNetDirectory = "$TempDirectory\dotnet-win" if (!(Test-Path variable:DotNetVersion)) { - ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } + ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } } else { - ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } + ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } } - - $env:PATH="$DotNetDirectory;$env:PATH" + $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" } -Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" +Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)" -ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile -- $BuildArguments } +ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } +ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } diff --git a/build.sh b/build.sh index 9532b4fbe0..76919a5351 100755 --- a/build.sh +++ b/build.sh @@ -1,16 +1,6 @@ #!/usr/bin/env bash -echo $(bash --version 2>&1 | head -n 1) - -#CUSTOMPARAM=0 -BUILD_ARGUMENTS=() -for i in "$@"; do - case $(echo $1 | awk '{print tolower($0)}') in - # -custom-param) CUSTOMPARAM=1;; - *) BUILD_ARGUMENTS+=("$1") ;; - esac - shift -done +bash --version 2>&1 | head -n 1 set -eo pipefail SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) @@ -20,11 +10,53 @@ SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) ########################################################################### BUILD_PROJECT_FILE="$SCRIPT_DIR/nukebuild/_build.csproj" +TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" + +DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" +DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" +DOTNET_CHANNEL="Current" export DOTNET_CLI_TELEMETRY_OPTOUT=1 export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -export NUGET_XMLDOC_MODE="skip" +export DOTNET_MULTILEVEL_LOOKUP=0 -dotnet --info +########################################################################### +# EXECUTION +########################################################################### -dotnet run --project "$BUILD_PROJECT_FILE" -- ${BUILD_ARGUMENTS[@]} +function FirstJsonValue { + perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" +} + +# If dotnet CLI is installed globally and it matches requested version, use for execution +if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then + export DOTNET_EXE="$(command -v dotnet)" +else + # Download install script + DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" + mkdir -p "$TEMP_DIRECTORY" + curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" + chmod +x "$DOTNET_INSTALL_FILE" + + # If global.json exists, load expected version + if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then + DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") + if [[ "$DOTNET_VERSION" == "" ]]; then + unset DOTNET_VERSION + fi + fi + + # Install by channel or version + DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" + if [[ -z ${DOTNET_VERSION+x} ]]; then + "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path + else + "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path + fi + export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" +fi + +echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)" + +"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet +"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" diff --git a/build/HarfBuzzSharp.props b/build/HarfBuzzSharp.props index 85e7a1f34d..620ec58ff3 100644 --- a/build/HarfBuzzSharp.props +++ b/build/HarfBuzzSharp.props @@ -1,7 +1,7 @@  - - - + + + diff --git a/build/NetAnalyzers.props b/build/NetAnalyzers.props new file mode 100644 index 0000000000..dfca9ecf9e --- /dev/null +++ b/build/NetAnalyzers.props @@ -0,0 +1,5 @@ + + + true + + diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props index c3b136d41d..1911c02677 100644 --- a/build/ReactiveUI.props +++ b/build/ReactiveUI.props @@ -1,5 +1,5 @@ - + diff --git a/build/SharedVersion.props b/build/SharedVersion.props index 3d9548ab9d..5838519596 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -2,13 +2,13 @@ xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> Avalonia - 0.10.999 + 11.0.999 Copyright 2022 © The AvaloniaUI Project https://avaloniaui.net https://github.com/AvaloniaUI/Avalonia/ true CS1591 - latest + preview MIT Icon.png Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS and with experimental support for Android, iOS and WebAssembly. diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index d54cffba08..31619399f9 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,7 +1,7 @@  - - - + + + diff --git a/dirs.proj b/dirs.proj index 396e0c915c..f1eaae8a4a 100644 --- a/dirs.proj +++ b/dirs.proj @@ -9,17 +9,17 @@ - - - - - + + + + + @@ -27,6 +27,6 @@ - + diff --git a/global.json b/global.json index a6792b05c7..a9318b212f 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.202", + "version": "7.0.100", "rollForward": "latestFeature" }, "msbuild-sdks": { diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index ebd9f39d30..4c7341f834 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -385,7 +385,7 @@ return true; } --(void)resignKeyWindow +-(void)windowDidResignKey:(NSNotification *)notification { if(_parent) _parent->BaseEvents->Deactivated(); @@ -393,8 +393,6 @@ [self showAppMenuOnly]; [self invalidateShadow]; - - [super resignKeyWindow]; } - (void)windowDidMove:(NSNotification *_Nonnull)notification diff --git a/native/Avalonia.Native/src/OSX/Screens.mm b/native/Avalonia.Native/src/OSX/Screens.mm index b9c75ed742..83ab1bfd01 100644 --- a/native/Avalonia.Native/src/OSX/Screens.mm +++ b/native/Avalonia.Native/src/OSX/Screens.mm @@ -41,9 +41,9 @@ public: ret->WorkingArea.X = [screen visibleFrame].origin.x; ret->WorkingArea.Y = ConvertPointY(ToAvnPoint([screen visibleFrame].origin)).Y - ret->WorkingArea.Height; - ret->PixelDensity = [screen backingScaleFactor]; + ret->Scaling = [screen backingScaleFactor]; - ret->Primary = index == 0; + ret->IsPrimary = index == 0; return S_OK; } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 95f61422cb..2443965957 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -63,7 +63,7 @@ HRESULT WindowImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { - _isDialog = isDialog; + _isDialog = isDialog || _parent != nullptr; WindowBaseImpl::Show(activate, isDialog); @@ -91,13 +91,13 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) { if(_parent != nullptr) { _parent->_children.remove(this); - - _parent->BringToFront(); } auto cparent = dynamic_cast(parent); _parent = cparent; + + _isDialog = _parent != nullptr; if(_parent != nullptr && Window != nullptr){ // If one tries to show a child window with a minimized parent window, then the parent window will be @@ -121,7 +121,7 @@ void WindowImpl::BringToFront() { if(Window != nullptr) { - if (![Window isMiniaturized]) + if ([Window isVisible] && ![Window isMiniaturized]) { if(IsDialog()) { diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index a15d0c9601..9cc9fc9523 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -95,11 +95,14 @@ ComPtr _events; } @end -extern void InitializeAvnApp(IAvnApplicationEvents* events) +extern void InitializeAvnApp(IAvnApplicationEvents* events, bool disableAppDelegate) { - NSApplication* app = [AvnApplication sharedApplication]; - id delegate = [[AvnAppDelegate alloc] initWithEvents:events]; - [app setDelegate:delegate]; + if(!disableAppDelegate) + { + NSApplication* app = [AvnApplication sharedApplication]; + id delegate = [[AvnAppDelegate alloc] initWithEvents:events]; + [app setDelegate:delegate]; + } } HRESULT AvnApplicationCommands::HideApp() diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index a90a235b9d..7ee7205776 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -32,7 +32,7 @@ extern void SetServicesMenu (IAvnMenu* menu); extern IAvnMenu* GetAppMenu (); extern NSMenuItem* GetAppMenuItem (); -extern void InitializeAvnApp(IAvnApplicationEvents* events); +extern void InitializeAvnApp(IAvnApplicationEvents* events, bool disableAppDelegate); extern NSApplicationActivationPolicy AvnDesiredActivationPolicy; extern NSPoint ToNSPoint (AvnPoint p); extern NSRect ToNSRect (AvnRect r); diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 6ee86b21ae..c29d4108d0 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -3,6 +3,8 @@ #include "common.h" static NSString* s_appTitle = @"Avalonia"; +static int disableSetProcessName = 0; +static bool disableAppDelegate = false; // Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be @@ -111,11 +113,17 @@ public: @autoreleasepool { auto appTitle = [NSString stringWithUTF8String: utf8String]; - - [[NSProcessInfo processInfo] setProcessName:appTitle]; - - - SetProcessName(appTitle); + if (disableSetProcessName == 0) + { + [[NSProcessInfo processInfo] setProcessName:appTitle]; + + SetProcessName(appTitle); + } + if (disableSetProcessName == 1) + { + auto rootMenu = [NSApp mainMenu]; + [rootMenu setTitle:appTitle]; + } return S_OK; } @@ -133,6 +141,27 @@ public: } } + virtual HRESULT SetDisableSetProcessName(int disable) override + { + START_COM_CALL; + + @autoreleasepool + { + disableSetProcessName = disable; + return S_OK; + } + } + + virtual HRESULT SetDisableAppDelegate(int disable) override + { + START_COM_CALL; + + @autoreleasepool { + disableAppDelegate = disable; + return S_OK; + } + } + }; /// See "Using POSIX Threads in a Cocoa Application" section here: @@ -175,7 +204,7 @@ public: @autoreleasepool{ [[ThreadingInitializer new] do]; } - InitializeAvnApp(events); + InitializeAvnApp(events, disableAppDelegate); return S_OK; }; diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index 2075cc85ab..1c22c91207 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -183,8 +183,11 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta [_layer setContents: (__bridge IOSurface*) surface->surface]; } [CATransaction commit]; - [CATransaction flush]; } + // This can trigger event processing on the main thread + // which might need to lock the renderer + // which can cause a deadlock. So flush call is outside of the lock + [CATransaction flush]; } else dispatch_async(dispatch_get_main_queue(), ^{ diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 9fcb9d6b7f..2295c0beda 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -23,6 +23,7 @@ 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 MicroCom.CodeGenerator; /* Before editing this file, install support plugin for your IDE, @@ -36,25 +37,6 @@ partial class Build : NukeBuild { [Solution("Avalonia.sln")] readonly Solution Solution; - static Lazy MsBuildExe = new Lazy(() => - { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return null; - - var msBuildDirectory = VSWhere("-latest -nologo -property installationPath -format value -prerelease").FirstOrDefault().Text; - - if (!string.IsNullOrWhiteSpace(msBuildDirectory)) - { - string msBuildExe = Path.Combine(msBuildDirectory, @"MSBuild\Current\Bin\MSBuild.exe"); - if (!System.IO.File.Exists(msBuildExe)) - msBuildExe = Path.Combine(msBuildDirectory, @"MSBuild\15.0\Bin\MSBuild.exe"); - - return msBuildExe; - } - - return null; - }, false); - BuildParameters Parameters { get; set; } protected override void OnBuildInitialized() { @@ -89,25 +71,28 @@ partial class Build : NukeBuild } ExecWait("dotnet version:", "dotnet", "--info"); ExecWait("dotnet workloads:", "dotnet", "workload list"); + Information("Processor count: " + Environment.ProcessorCount); + Information("Available RAM: " + GC.GetGCMemoryInfo().TotalAvailableMemoryBytes / 0x100000 + "MB"); } - IReadOnlyCollection MsBuildCommon( - string projectFile, - Configure configurator = null) + DotNetConfigHelper ApplySettingCore(DotNetConfigHelper c) { - return MSBuild(c => c - .SetProjectFile(projectFile) - // This is required for VS2019 image on Azure Pipelines - .When(Parameters.IsRunningOnWindows && - Parameters.IsRunningOnAzure, _ => _ - .AddProperty("JavaSdkDirectory", GetVariable("JAVA_HOME_11_X64"))) - .AddProperty("PackageVersion", Parameters.Version) + if (Parameters.IsRunningOnAzure) + c.AddProperty("JavaSdkDirectory", GetVariable("JAVA_HOME_11_X64")); + c.AddProperty("PackageVersion", Parameters.Version) .AddProperty("iOSRoslynPathHackRequired", true) - .SetProcessToolPath(MsBuildExe.Value) .SetConfiguration(Parameters.Configuration) - .SetVerbosity(MSBuildVerbosity.Minimal) - .Apply(configurator)); + .SetVerbosity(DotNetVerbosity.Minimal); + return c; } + DotNetBuildSettings ApplySetting(DotNetBuildSettings c, Configure configurator = null) => + ApplySettingCore(c).Build.Apply(configurator); + + DotNetPackSettings ApplySetting(DotNetPackSettings c, Configure configurator = null) => + ApplySettingCore(c).Pack.Apply(configurator); + + DotNetTestSettings ApplySetting(DotNetTestSettings c, Configure configurator = null) => + ApplySettingCore(c).Test.Apply(configurator); Target Clean => _ => _.Executes(() => { @@ -149,28 +134,36 @@ partial class Build : NukeBuild Target Compile => _ => _ .DependsOn(Clean, CompileNative) .DependsOn(CompileHtmlPreviewer) - .Executes(async () => + .Executes(() => { - if (Parameters.IsRunningOnWindows) - MsBuildCommon(Parameters.MSBuildSolution, c => c - .SetProcessArgumentConfigurator(a => a.Add("/r")) - .AddTargets("Build") - ); - - else - DotNetBuild(c => c - .SetProjectFile(Parameters.MSBuildSolution) - .AddProperty("PackageVersion", Parameters.Version) - .SetConfiguration(Parameters.Configuration) - ); + DotNetBuild(c => ApplySetting(c) + .SetProjectFile(Parameters.MSBuildSolution) + ); }); void RunCoreTest(string projectName) { Information($"Running tests from {projectName}"); var project = Solution.GetProject(projectName).NotNull("project != null"); + // Nuke and MSBuild tools have build-in helpers to get target frameworks from the project. + // Unfortunately, it gets broken with every second SDK update, so we had to do it manually. + var fileXml = XDocument.Parse(File.ReadAllText(project.Path)); + var targetFrameworks = fileXml.Descendants("TargetFrameworks") + .FirstOrDefault()?.Value.Split(';').Select(f => f.Trim()); + if (targetFrameworks is null) + { + var targetFramework = fileXml.Descendants("TargetFramework").FirstOrDefault()?.Value; + if (targetFramework is not null) + { + targetFrameworks = new[] { targetFramework }; + } + } + if (targetFrameworks is null) + { + throw new InvalidOperationException("No target frameworks were found in the test project"); + } - foreach (var fw in project.GetTargetFrameworks()) + foreach (var fw in targetFrameworks) { if (fw.StartsWith("net4") && RuntimeInformation.IsOSPlatform(OSPlatform.Linux) @@ -182,14 +175,13 @@ partial class Build : NukeBuild Information($"Running for {projectName} ({fw}) ..."); - DotNetTest(c => c + DotNetTest(c => ApplySetting(c) .SetProjectFile(project) - .SetConfiguration(Parameters.Configuration) .SetFramework(fw) .EnableNoBuild() .EnableNoRestore() .When(Parameters.PublishTestResults, _ => _ - .SetLogger("trx") + .SetLoggers("trx") .SetResultsDirectory(Parameters.TestResultsRoot))); } } @@ -241,8 +233,6 @@ partial class Build : NukeBuild RunCoreTest("Avalonia.DesignerSupport.Tests"); }); - [PackageExecutable("JetBrains.dotMemoryUnit", "dotMemoryUnit.exe")] readonly Tool DotMemoryUnit; - Target RunLeakTests => _ => _ .OnlyWhenStatic(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows) .DependsOn(Compile) @@ -250,12 +240,9 @@ partial class Build : NukeBuild { void DoMemoryTest() { - var testAssembly = "tests\\Avalonia.LeakTests\\bin\\Release\\net461\\Avalonia.LeakTests.dll"; - DotMemoryUnit( - $"{XunitPath.DoubleQuoteIfNeeded()} --propagate-exit-code -- {testAssembly}", - timeout: 120_000); + RunCoreTest("Avalonia.LeakTests"); } - ControlFlow.ExecuteWithRetry(DoMemoryTest, waitInSeconds: 3); + ControlFlow.ExecuteWithRetry(DoMemoryTest, delay: TimeSpan.FromMilliseconds(3)); }); Target ZipFiles => _ => _ @@ -263,19 +250,7 @@ partial class Build : NukeBuild .Executes(() => { var data = Parameters; - var pathToProjectSource = RootDirectory / "samples" / "ControlCatalog.NetCore"; - var pathToPublish = pathToProjectSource / "bin" / data.Configuration / "publish"; - - DotNetPublish(c => c - .SetProject(pathToProjectSource / "ControlCatalog.NetCore.csproj") - .EnableNoBuild() - .SetConfiguration(data.Configuration) - .AddProperty("PackageVersion", data.Version) - .AddProperty("PublishDir", pathToPublish)); - - Zip(data.ZipCoreArtifacts, data.BinRoot); Zip(data.ZipNuGetArtifacts, data.NugetRoot); - Zip(data.ZipTargetControlCatalogNetCoreDir, pathToPublish); }); Target CreateIntermediateNugetPackages => _ => _ @@ -283,15 +258,7 @@ partial class Build : NukeBuild .After(RunTests) .Executes(() => { - if (Parameters.IsRunningOnWindows) - - MsBuildCommon(Parameters.MSBuildSolution, c => c - .AddTargets("Pack")); - else - DotNetPack(c => c - .SetProject(Parameters.MSBuildSolution) - .SetConfiguration(Parameters.Configuration) - .AddProperty("PackageVersion", Parameters.Version)); + DotNetPack(c => ApplySetting(c).SetProject(Parameters.MSBuildSolution)); }); Target CreateNugetPackages => _ => _ @@ -329,6 +296,14 @@ partial class Build : NukeBuild .DependsOn(Package) .DependsOn(ZipFiles); + Target GenerateCppHeaders => _ => _.Executes(() => + { + var file = MicroComCodeGenerator.Parse( + File.ReadAllText(RootDirectory / "src" / "Avalonia.Native" / "avn.idl")); + File.WriteAllText(RootDirectory / "native" / "Avalonia.Native" / "inc" / "avalonia-native.h", + file.GenerateCppHeader()); + }); + public static int Main() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index a92c988fbd..dfa914d1db 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -51,14 +51,12 @@ public partial class Build public AbsolutePath NugetIntermediateRoot { get; } public AbsolutePath NugetRoot { get; } public AbsolutePath ZipRoot { get; } - public AbsolutePath BinRoot { get; } public AbsolutePath TestResultsRoot { get; } public string DirSuffix { get; } public List BuildDirs { get; } public string FileZipSuffix { get; } public AbsolutePath ZipCoreArtifacts { get; } public AbsolutePath ZipNuGetArtifacts { get; } - public AbsolutePath ZipTargetControlCatalogNetCoreDir { get; } public BuildParameters(Build b) @@ -76,11 +74,11 @@ public partial class Build MSBuildSolution = RootDirectory / "dirs.proj"; // PARAMETERS - IsLocalBuild = Host == HostType.Console; + IsLocalBuild = NukeBuild.IsLocalBuild; IsRunningOnUnix = Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX; IsRunningOnWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - IsRunningOnAzure = Host == HostType.AzurePipelines || + IsRunningOnAzure = Host is AzurePipelines || Environment.GetEnvironmentVariable("LOGNAME") == "vsts"; if (IsRunningOnAzure) @@ -121,14 +119,12 @@ public partial class Build NugetRoot = ArtifactsDir / "nuget"; NugetIntermediateRoot = RootDirectory / "build-intermediate" / "nuget"; ZipRoot = ArtifactsDir / "zip"; - BinRoot = ArtifactsDir / "bin"; TestResultsRoot = ArtifactsDir / "test-results"; BuildDirs = GlobDirectories(RootDirectory, "**bin").Concat(GlobDirectories(RootDirectory, "**obj")).ToList(); DirSuffix = Configuration; FileZipSuffix = Version + ".zip"; ZipCoreArtifacts = ZipRoot / ("Avalonia-" + FileZipSuffix); ZipNuGetArtifacts = ZipRoot / ("Avalonia-NuGet-" + FileZipSuffix); - ZipTargetControlCatalogNetCoreDir = ZipRoot / ("ControlCatalog.NetCore-" + FileZipSuffix); } string GetVersion() diff --git a/nukebuild/BuildTasksPatcher.cs b/nukebuild/BuildTasksPatcher.cs index e3766ae23f..5fd331035a 100644 --- a/nukebuild/BuildTasksPatcher.cs +++ b/nukebuild/BuildTasksPatcher.cs @@ -17,8 +17,12 @@ public class BuildTasksPatcher { if (entry.Name == "Avalonia.Build.Tasks.dll") { - var temp = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".dll"); + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + var temp = Path.Combine(tempDir, Guid.NewGuid() + ".dll"); var output = temp + ".output"; + File.Copy(typeof(Microsoft.Build.Framework.ITask).Assembly.GetModules()[0].FullyQualifiedName, + Path.Combine(tempDir, "Microsoft.Build.Framework.dll")); var patched = new MemoryStream(); try { @@ -57,10 +61,8 @@ public class BuildTasksPatcher { try { - if (File.Exists(temp)) - File.Delete(temp); - if (File.Exists(output)) - File.Delete(output); + if(Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); } catch { diff --git a/nukebuild/DotNetConfigHelper.cs b/nukebuild/DotNetConfigHelper.cs new file mode 100644 index 0000000000..eca1e2684d --- /dev/null +++ b/nukebuild/DotNetConfigHelper.cs @@ -0,0 +1,57 @@ +using System.Globalization; +using JetBrains.Annotations; +using Nuke.Common.Tools.DotNet; +// ReSharper disable ReturnValueOfPureMethodIsNotUsed + +public class DotNetConfigHelper +{ + public DotNetBuildSettings Build; + public DotNetPackSettings Pack; + public DotNetTestSettings Test; + + public DotNetConfigHelper(DotNetBuildSettings s) + { + Build = s; + } + + public DotNetConfigHelper(DotNetPackSettings s) + { + Pack = s; + } + + public DotNetConfigHelper(DotNetTestSettings s) + { + Test = s; + } + + public DotNetConfigHelper AddProperty(string key, bool value) => + AddProperty(key, value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()); + public DotNetConfigHelper AddProperty(string key, string value) + { + Build = Build?.AddProperty(key, value); + Pack = Pack?.AddProperty(key, value); + Test = Test?.AddProperty(key, value); + + return this; + } + + public DotNetConfigHelper SetConfiguration(string configuration) + { + Build = Build?.SetConfiguration(configuration); + Pack = Pack?.SetConfiguration(configuration); + Test = Test?.SetConfiguration(configuration); + return this; + } + + public DotNetConfigHelper SetVerbosity(DotNetVerbosity verbosity) + { + Build = Build?.SetVerbosity(verbosity); + Pack = Pack?.SetVerbosity(verbosity); + Test = Test?.SetVerbosity(verbosity); + return this; + } + + public static implicit operator DotNetConfigHelper(DotNetBuildSettings s) => new DotNetConfigHelper(s); + public static implicit operator DotNetConfigHelper(DotNetPackSettings s) => new DotNetConfigHelper(s); + public static implicit operator DotNetConfigHelper(DotNetTestSettings s) => new DotNetConfigHelper(s); +} diff --git a/nukebuild/MicroComGen.cs b/nukebuild/MicroComGen.cs deleted file mode 100644 index b1e546cb97..0000000000 --- a/nukebuild/MicroComGen.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.IO; -using MicroCom.CodeGenerator; -using Nuke.Common; - -partial class Build : NukeBuild -{ - Target GenerateCppHeaders => _ => _.Executes(() => - { - var file = MicroComCodeGenerator.Parse( - File.ReadAllText(RootDirectory / "src" / "Avalonia.Native" / "avn.idl")); - File.WriteAllText(RootDirectory / "native" / "Avalonia.Native" / "inc" / "avalonia-native.h", - file.GenerateCppHeader()); - }); -} \ No newline at end of file diff --git a/nukebuild/Shims.cs b/nukebuild/Shims.cs index 1ac14bf622..6f79972ad6 100644 --- a/nukebuild/Shims.cs +++ b/nukebuild/Shims.cs @@ -49,7 +49,11 @@ public partial class Build { if (fsEntry is FileInfo) { +#if NET6 var relPath = Path.GetRelativePath(rootPath, fsEntry.FullName); +#else + var relPath = GetRelativePath(rootPath, fsEntry.FullName); +#endif AddFile(fsEntry.FullName, relPath); } } @@ -78,6 +82,17 @@ public partial class Build } } + private static string GetRelativePath(string relativeTo, string path) + { + var uri = new Uri(relativeTo); + var rel = Uri.UnescapeDataString(uri.MakeRelativeUri(new Uri(path)).ToString()).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + if (rel.Contains(Path.DirectorySeparatorChar.ToString()) == false) + { + rel = $".{Path.DirectorySeparatorChar}{rel}"; + } + return rel; + } + class NumergeNukeLogger : INumergeLogger { public void Log(NumergeLogLevel level, string message) diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index b2c58e2292..92d9732e91 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -1,41 +1,48 @@  - Exe - netcoreapp3.1 false False - CS0649;CS0169 + CS0649;CS0169;SYSLIB0011 + 1 + net7.0 + - - + - - + - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - - - - - - - - - - + + + + + + + + + + + + diff --git a/nukebuild/il-repack b/nukebuild/il-repack new file mode 160000 index 0000000000..892f079ea8 --- /dev/null +++ b/nukebuild/il-repack @@ -0,0 +1 @@ +Subproject commit 892f079ea8cb0c178f0a68f53a7a7eac13acdda9 diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index d43a5c1624..01bf303a20 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -3,8 +3,8 @@ <_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild) <_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false low - <_AvaloniaPatchComInterop Condition="'$(_AvaloniaPatchComInterop)' == ''">false <_AvaloniaSkipXamlCompilation Condition="'$(_AvaloniaSkipXamlCompilation)' == ''">false + false @@ -44,7 +44,7 @@ $(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences;_GenerateAvaloniaResourcesDependencyCache - + @@ -71,7 +71,6 @@ Output="$(AvaloniaResourcesTemporaryFilePath)" Root="$(MSBuildProjectDirectory)" Resources="@(AvaloniaResource)" - EmbeddedResources="@(EmbeddedResources)" ReportImportance="$(AvaloniaXamlReportImportance)"/> - + diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs index 33ca511340..62c582610c 100644 --- a/samples/ControlCatalog.Android/MainActivity.cs +++ b/samples/ControlCatalog.Android/MainActivity.cs @@ -5,16 +5,8 @@ using Avalonia.Android; namespace ControlCatalog.Android { - [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] - public class MainActivity : AvaloniaActivity + [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)] + public class MainActivity : AvaloniaMainActivity { - protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) - { - return base.CustomizeAppBuilder(builder) - .AfterSetup(_ => - { - Pages.EmbedSample.Implementation = new EmbedSampleAndroid(); - }); - } } } diff --git a/samples/ControlCatalog.Android/SplashActivity.cs b/samples/ControlCatalog.Android/SplashActivity.cs index dc292fd37b..908b5f082a 100644 --- a/samples/ControlCatalog.Android/SplashActivity.cs +++ b/samples/ControlCatalog.Android/SplashActivity.cs @@ -1,12 +1,23 @@ using Android.App; using Android.Content; +using Android.Content.PM; using Android.OS; +using Avalonia.Android; namespace ControlCatalog.Android { [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)] - public class SplashActivity : Activity + public class SplashActivity : AvaloniaSplashActivity { + protected override Avalonia.AppBuilder CustomizeAppBuilder(Avalonia.AppBuilder builder) + { + return base.CustomizeAppBuilder(builder) + .AfterSetup(_ => + { + Pages.EmbedSample.Implementation = new EmbedSampleAndroid(); + }); + } + protected override void OnCreate(Bundle? savedInstanceState) { base.OnCreate(savedInstanceState); diff --git a/samples/ControlCatalog.Web/App.razor b/samples/ControlCatalog.Blazor.Web/App.razor similarity index 100% rename from samples/ControlCatalog.Web/App.razor rename to samples/ControlCatalog.Blazor.Web/App.razor diff --git a/samples/ControlCatalog.Blazor.Web/App.razor.cs b/samples/ControlCatalog.Blazor.Web/App.razor.cs new file mode 100644 index 0000000000..8cc0095f20 --- /dev/null +++ b/samples/ControlCatalog.Blazor.Web/App.razor.cs @@ -0,0 +1,17 @@ +using Avalonia; +using Avalonia.Web.Blazor; + +namespace ControlCatalog.Blazor.Web; + +public partial class App +{ + protected override void OnParametersSet() + { + AppBuilder.Configure() + .UseBlazor() + // .With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering + .SetupWithSingleViewLifetime(); + + base.OnParametersSet(); + } +} diff --git a/samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj b/samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj new file mode 100644 index 0000000000..03fb31f0d3 --- /dev/null +++ b/samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj @@ -0,0 +1,29 @@ + + + net7.0 + browser-wasm + enable + 16777216 + false + false + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ControlCatalog.Web/Pages/Index.razor b/samples/ControlCatalog.Blazor.Web/Pages/Index.razor similarity index 100% rename from samples/ControlCatalog.Web/Pages/Index.razor rename to samples/ControlCatalog.Blazor.Web/Pages/Index.razor diff --git a/samples/ControlCatalog.Blazor.Web/Program.cs b/samples/ControlCatalog.Blazor.Web/Program.cs new file mode 100644 index 0000000000..d71b125fa1 --- /dev/null +++ b/samples/ControlCatalog.Blazor.Web/Program.cs @@ -0,0 +1,29 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.Extensions.DependencyInjection; +using ControlCatalog.Blazor.Web; + +public class Program +{ + public static async Task Main(string[] args) + { + await CreateHostBuilder(args).Build().RunAsync(); + } + + 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.Web/Properties/launchSettings.json b/samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json similarity index 66% rename from samples/ControlCatalog.Web/Properties/launchSettings.json rename to samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json index e4da60f7ca..ad2b1e30f6 100644 --- a/samples/ControlCatalog.Web/Properties/launchSettings.json +++ b/samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json @@ -8,14 +8,6 @@ } }, "profiles": { - "ControlCatalog.Web - IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, "ControlCatalog.Web": { "commandName": "Project", "dotnetRunMessages": "true", diff --git a/samples/ControlCatalog.Web/Shared/MainLayout.razor b/samples/ControlCatalog.Blazor.Web/Shared/MainLayout.razor similarity index 100% rename from samples/ControlCatalog.Web/Shared/MainLayout.razor rename to samples/ControlCatalog.Blazor.Web/Shared/MainLayout.razor diff --git a/samples/ControlCatalog.Web/_Imports.razor b/samples/ControlCatalog.Blazor.Web/_Imports.razor similarity index 85% rename from samples/ControlCatalog.Web/_Imports.razor rename to samples/ControlCatalog.Blazor.Web/_Imports.razor index 04c7a8690e..0e6d11b419 100644 --- a/samples/ControlCatalog.Web/_Imports.razor +++ b/samples/ControlCatalog.Blazor.Web/_Imports.razor @@ -6,6 +6,5 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.WebAssembly.Http @using Microsoft.JSInterop -@using ControlCatalog.Web -@using ControlCatalog.Web.Shared +@using ControlCatalog.Blazor.Web.Shared @using SkiaSharp diff --git a/samples/ControlCatalog.Web/wwwroot/css/app.css b/samples/ControlCatalog.Blazor.Web/wwwroot/css/app.css similarity index 100% rename from samples/ControlCatalog.Web/wwwroot/css/app.css rename to samples/ControlCatalog.Blazor.Web/wwwroot/css/app.css diff --git a/samples/ControlCatalog.Web/wwwroot/favicon.ico b/samples/ControlCatalog.Blazor.Web/wwwroot/favicon.ico similarity index 100% rename from samples/ControlCatalog.Web/wwwroot/favicon.ico rename to samples/ControlCatalog.Blazor.Web/wwwroot/favicon.ico diff --git a/samples/ControlCatalog.Web/wwwroot/index.html b/samples/ControlCatalog.Blazor.Web/wwwroot/index.html similarity index 93% rename from samples/ControlCatalog.Web/wwwroot/index.html rename to samples/ControlCatalog.Blazor.Web/wwwroot/index.html index 7ea600673a..cad9123836 100644 --- a/samples/ControlCatalog.Web/wwwroot/index.html +++ b/samples/ControlCatalog.Blazor.Web/wwwroot/index.html @@ -17,7 +17,6 @@ Reload 🗙 - diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 0667644643..e4c83dca49 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -5,6 +5,7 @@ net6.0 true true + 6.0.8 diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index d98a068d84..b1bacc6483 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -115,10 +115,6 @@ namespace ControlCatalog.NetCore UseDBusMenu = true, EnableIme = true }) - .With(new Win32PlatformOptions - { - EnableMultitouch = true - }) .UseSkia() .AfterSetup(builder => { diff --git a/samples/ControlCatalog.Web/App.razor.cs b/samples/ControlCatalog.Web/App.razor.cs deleted file mode 100644 index 560e8079a6..0000000000 --- a/samples/ControlCatalog.Web/App.razor.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Avalonia.Web.Blazor; - -namespace ControlCatalog.Web; - -public partial class App -{ - protected override void OnParametersSet() - { - WebAppBuilder.Configure() - .AfterSetup(_ => - { - ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb(); - }) - //.With(new SkiaOptions { CustomGpuFactory = null }) // uncomment to disable GPU/GL rendering - .SetupWithSingleViewLifetime(); - - base.OnParametersSet(); - } -} diff --git a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj index b2c9ec72eb..06a5466619 100644 --- a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj +++ b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj @@ -1,57 +1,44 @@ - + - net6.0 - enable - - true - 16777216 - false - false + net7.0 + browser-wasm + main.js + Exe + true + true + true + -sVERBOSE -sERROR_ON_UNDEFINED_SYMBOLS=0 - - - false - -O1 - false - - - - true - true - -O3 - -O3 - false - false - false - false - false - false - true - false - true - true + + true true - link - true + full + true + true + -O2 + -O2 - - + - + - - - - - + + + + + + + + + + - diff --git a/samples/ControlCatalog.Web/EmbedSample.Browser.cs b/samples/ControlCatalog.Web/EmbedSample.Browser.cs index 5fe14409de..5cfbb608cc 100644 --- a/samples/ControlCatalog.Web/EmbedSample.Browser.cs +++ b/samples/ControlCatalog.Web/EmbedSample.Browser.cs @@ -1,34 +1,42 @@ using System; - -using Avalonia; +using System.Runtime.InteropServices.JavaScript; using Avalonia.Platform; -using Avalonia.Web.Blazor; +using Avalonia.Web; using ControlCatalog.Pages; -using Microsoft.JSInterop; - namespace ControlCatalog.Web; public class EmbedSampleWeb : INativeDemoControl { public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) { - var runtime = AvaloniaLocator.Current.GetRequiredService(); - if (isSecond) { - var iframe = runtime.Invoke("document.createElement", "iframe"); - iframe.InvokeVoid("setAttribute", "src", "https://www.youtube.com/embed/kZCIporjJ70"); + var iframe = EmbedInterop.CreateElement("iframe"); + iframe.SetProperty("src", "https://www.youtube.com/embed/kZCIporjJ70"); return new JSObjectControlHandle(iframe); } else { - // window.createAppButton source is defined in "app.js" file. - var button = runtime.Invoke("window.createAppButton"); + var defaultHandle = (JSObjectControlHandle)createDefault(); + + _ = JSHost.ImportAsync("embed.js", "./embed.js").ContinueWith(_ => + { + EmbedInterop.AddAppButton(defaultHandle.Object); + }); - return new JSObjectControlHandle(button); + return defaultHandle; } } } + +internal static partial class EmbedInterop +{ + [JSImport("globalThis.document.createElement")] + public static partial JSObject CreateElement(string tagName); + + [JSImport("addAppButton", "embed.js")] + public static partial void AddAppButton(JSObject parentObject); +} diff --git a/samples/ControlCatalog.Web/Logo.svg b/samples/ControlCatalog.Web/Logo.svg new file mode 100644 index 0000000000..9685a23af1 --- /dev/null +++ b/samples/ControlCatalog.Web/Logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/samples/ControlCatalog.Web/Program.cs b/samples/ControlCatalog.Web/Program.cs index d1a7925813..7d05c8e462 100644 --- a/samples/ControlCatalog.Web/Program.cs +++ b/samples/ControlCatalog.Web/Program.cs @@ -1,29 +1,22 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.Extensions.DependencyInjection; +using System.Runtime.Versioning; +using Avalonia; +using Avalonia.Web; +using ControlCatalog; using ControlCatalog.Web; -public class Program +[assembly:SupportedOSPlatform("browser")] + +internal partial class Program { - public static async Task Main(string[] args) + private static void Main(string[] args) { - await CreateHostBuilder(args).Build().RunAsync(); + BuildAvaloniaApp() + .AfterSetup(_ => + { + ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb(); + }).SetupBrowserApp("out"); } - 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; - } + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure(); } - - - - diff --git a/samples/ControlCatalog.Web/Roots.xml b/samples/ControlCatalog.Web/Roots.xml new file mode 100644 index 0000000000..b07fd86fa2 --- /dev/null +++ b/samples/ControlCatalog.Web/Roots.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/samples/ControlCatalog.Web/app.css b/samples/ControlCatalog.Web/app.css new file mode 100644 index 0000000000..27680f6e1a --- /dev/null +++ b/samples/ControlCatalog.Web/app.css @@ -0,0 +1,49 @@ +#out { + height: 100vh; + width: 100vw +} + +#avalonia-splash { + position: relative; + height: 100%; + width: 100%; + color: whitesmoke; + background: #171C2C; + font-family: 'Nunito', sans-serif; + background-position: center; + background-size: cover; + background-repeat: no-repeat; +} + +#avalonia-splash a{ + color: whitesmoke; + text-decoration: none; +} + +.center { + display: flex; + justify-content: center; + height: 250px; +} + +.splash-close { + animation: slide 0.5s linear 1s forwards; +} + +@keyframes slide { + 0% { + top: 0%; + } + + 50% { + opacity: 80%; + } + + 100% { + top: 100%; + overflow: hidden; + opacity: 0; + display: none; + visibility: collapse; + } +} diff --git a/samples/ControlCatalog.Web/wwwroot/js/app.js b/samples/ControlCatalog.Web/embed.js similarity index 59% rename from samples/ControlCatalog.Web/wwwroot/js/app.js rename to samples/ControlCatalog.Web/embed.js index 29697661a6..f393c80314 100644 --- a/samples/ControlCatalog.Web/wwwroot/js/app.js +++ b/samples/ControlCatalog.Web/embed.js @@ -1,10 +1,11 @@ -window.createAppButton = function () { - var button = document.createElement('button'); +export function addAppButton(parent) { + var button = globalThis.document.createElement('button'); button.innerText = 'Hello world'; var clickCount = 0; button.onclick = () => { clickCount++; button.innerText = 'Click count ' + clickCount; }; + parent.appendChild(button); return button; } diff --git a/samples/ControlCatalog.Web/favicon.ico b/samples/ControlCatalog.Web/favicon.ico new file mode 100644 index 0000000000..da8d49ff9b Binary files /dev/null and b/samples/ControlCatalog.Web/favicon.ico differ diff --git a/samples/ControlCatalog.Web/index.html b/samples/ControlCatalog.Web/index.html new file mode 100644 index 0000000000..226ae70695 --- /dev/null +++ b/samples/ControlCatalog.Web/index.html @@ -0,0 +1,31 @@ + + + + + + + AvaloniaUI - ControlCatalog + + + + + + + + + +
+
+
+

Powered by

+ + Avalonia Logo + Avalonia + +
+
+
+ + + + diff --git a/samples/ControlCatalog.Web/main.js b/samples/ControlCatalog.Web/main.js new file mode 100644 index 0000000000..87f8a4f943 --- /dev/null +++ b/samples/ControlCatalog.Web/main.js @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { dotnet } from './dotnet.js' +import { registerAvaloniaModule } from './avalonia.js'; + +const is_browser = typeof window != "undefined"; +if (!is_browser) throw new Error(`Expected to be running in a browser`); + +const dotnetRuntime = await dotnet + .withDiagnosticTracing(false) + .withApplicationArgumentsFromQuery() + .create(); + +await registerAvaloniaModule(dotnetRuntime); + +const config = dotnetRuntime.getConfig(); + +await dotnetRuntime.runMainAndExit(config.mainAssemblyName, ["dotnet", "is", "great!"]); diff --git a/samples/ControlCatalog.Web/runtimeconfig.template.json b/samples/ControlCatalog.Web/runtimeconfig.template.json new file mode 100644 index 0000000000..8f0557352c --- /dev/null +++ b/samples/ControlCatalog.Web/runtimeconfig.template.json @@ -0,0 +1,11 @@ +{ + "wasmHostProperties": { + "perHostConfig": [ + { + "name": "browser", + "html-path": "index.html", + "Host": "browser" + } + ] + } +} diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj index 513ac44f83..74d5b2fd8c 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj +++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj @@ -9,6 +9,9 @@ iossimulator-x64
+ + iPhone Developer + diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 7ebb87094a..750c1082a6 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -5,7 +5,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml.Styling; using Avalonia.Styling; -using Avalonia.Themes.Default; +using Avalonia.Themes.Simple; using Avalonia.Themes.Fluent; using ControlCatalog.ViewModels; @@ -23,9 +23,9 @@ namespace ControlCatalog Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml") }; - public static readonly StyleInclude ColorPickerDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) + public static readonly StyleInclude ColorPickerSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { - Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml") + Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml") }; public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) @@ -33,16 +33,16 @@ namespace ControlCatalog Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml") }; - public static readonly StyleInclude DataGridDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) + public static readonly StyleInclude DataGridSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { - Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Default.xaml") + Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml") }; public static FluentTheme Fluent = new FluentTheme(new Uri("avares://ControlCatalog/Styles")); - public static SimpleTheme Default = new SimpleTheme(new Uri("avares://ControlCatalog/Styles")); + public static SimpleTheme Simple = new SimpleTheme(new Uri("avares://ControlCatalog/Styles")); - public static Styles DefaultLight = new Styles + public static Styles SimpleLight = new Styles { new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) { @@ -56,10 +56,10 @@ namespace ControlCatalog { Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml") }, - Default + Simple }; - public static Styles DefaultDark = new Styles + public static Styles SimpleDark = new Styles { new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) { @@ -73,7 +73,7 @@ namespace ControlCatalog { Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml") }, - Default + Simple }; public override void Initialize() diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 8358fb3cd4..2654574a3e 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -29,7 +29,7 @@ - + diff --git a/samples/ControlCatalog/Converter/HexConverter.cs b/samples/ControlCatalog/Converter/HexConverter.cs new file mode 100644 index 0000000000..31cce5ba67 --- /dev/null +++ b/samples/ControlCatalog/Converter/HexConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Globalization; +using Avalonia; +using Avalonia.Data.Converters; + +namespace ControlCatalog.Converter; + +public class HexConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + var str = value?.ToString(); + if (str == null) + return AvaloniaProperty.UnsetValue; + if (int.TryParse(str, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int x)) + return (decimal)x; + return AvaloniaProperty.UnsetValue; + + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + try + { + if (value is decimal d) + return ((int)d).ToString("X8"); + return AvaloniaProperty.UnsetValue; + } + catch + { + return AvaloniaProperty.UnsetValue; + } + } +} diff --git a/samples/ControlCatalog/DecoratedWindow.xaml b/samples/ControlCatalog/DecoratedWindow.xaml index 5251a2fa55..c778b31c42 100644 --- a/samples/ControlCatalog/DecoratedWindow.xaml +++ b/samples/ControlCatalog/DecoratedWindow.xaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ControlCatalog.DecoratedWindow" Title="Avalonia Control Gallery" - xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False" Name="Window"> + xmlns:local="clr-namespace:ControlCatalog" SystemDecorations="None" Name="Window"> diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 7461e78c33..0d7a1ea4d7 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -12,13 +12,16 @@ - - + + + + + @@ -115,6 +118,9 @@ + + + @@ -127,7 +133,7 @@ - + @@ -187,8 +193,8 @@ FluentLight FluentDark - DefaultLight - DefaultDark + SimpleLight + SimpleDark ("Sidebar"); - if (AvaloniaLocator.Current?.GetService()?.GetRuntimeInfo().IsDesktop == true) + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime) { - IList tabItems = ((IList)sideBar.Items); - tabItems.Add(new TabItem() + var tabItems = (sideBar.Items as IList); + tabItems?.Add(new TabItem() { Header = "Screens", Content = new ScreenPage() }); - } var themes = this.Get("Themes"); @@ -36,7 +35,7 @@ namespace ControlCatalog { if (themes.SelectedItem is CatalogTheme theme) { - var themeStyle = Application.Current.Styles[0]; + var themeStyle = Application.Current!.Styles[0]; if (theme == CatalogTheme.FluentLight) { if (App.Fluent.Mode != FluentThemeMode.Light) @@ -58,19 +57,19 @@ namespace ControlCatalog Application.Current.Styles[1] = App.ColorPickerFluent; Application.Current.Styles[2] = App.DataGridFluent; } - else if (theme == CatalogTheme.DefaultLight) + else if (theme == CatalogTheme.SimpleLight) { - App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Light; - Application.Current.Styles[0] = App.DefaultLight; - Application.Current.Styles[1] = App.ColorPickerDefault; - Application.Current.Styles[2] = App.DataGridDefault; + App.Simple.Mode = Avalonia.Themes.Simple.SimpleThemeMode.Light; + Application.Current.Styles[0] = App.SimpleLight; + Application.Current.Styles[1] = App.ColorPickerSimple; + Application.Current.Styles[2] = App.DataGridSimple; } - else if (theme == CatalogTheme.DefaultDark) + else if (theme == CatalogTheme.SimpleDark) { - App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Dark; - Application.Current.Styles[0] = App.DefaultDark; - Application.Current.Styles[1] = App.ColorPickerDefault; - Application.Current.Styles[2] = App.DataGridDefault; + App.Simple.Mode = Avalonia.Themes.Simple.SimpleThemeMode.Dark; + Application.Current.Styles[0] = App.SimpleDark; + Application.Current.Styles[1] = App.ColorPickerSimple; + Application.Current.Styles[2] = App.DataGridSimple; } } }; diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index 1e4bf2de38..d5513904c0 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -18,15 +18,15 @@ - - + + + Click="OnCloseClicked" /> diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index c61296ac8f..c589f41442 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -11,23 +11,13 @@ namespace ControlCatalog { public class MainWindow : Window { - private WindowNotificationManager _notificationArea; private NativeMenu? _recentMenu; public MainWindow() { this.InitializeComponent(); - //Renderer.DrawFps = true; - //Renderer.DrawDirtyRects = Renderer.DrawFps = true; - - _notificationArea = new WindowNotificationManager(this) - { - Position = NotificationPosition.TopRight, - MaxItems = 3 - }; - - DataContext = new MainWindowViewModel(_notificationArea); + DataContext = new MainWindowViewModel(); _recentMenu = ((NativeMenu.GetMenu(this)?.Items[0] as NativeMenuItem)?.Menu?.Items[2] as NativeMenuItem)?.Menu; } diff --git a/samples/ControlCatalog/Models/CatalogTheme.cs b/samples/ControlCatalog/Models/CatalogTheme.cs index f7be8e0014..37224ed26e 100644 --- a/samples/ControlCatalog/Models/CatalogTheme.cs +++ b/samples/ControlCatalog/Models/CatalogTheme.cs @@ -1,14 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace ControlCatalog.Models +namespace ControlCatalog.Models { public enum CatalogTheme { FluentLight, FluentDark, - DefaultLight, - DefaultDark + SimpleLight, + SimpleDark } } diff --git a/samples/ControlCatalog/Models/Person.cs b/samples/ControlCatalog/Models/Person.cs index 2dfa02c7ed..99bc50250b 100644 --- a/samples/ControlCatalog/Models/Person.cs +++ b/samples/ControlCatalog/Models/Person.cs @@ -85,7 +85,7 @@ namespace ControlCatalog.Models } else { - if (_errorLookup.TryGetValue(propertyName, out List errorList)) + if (_errorLookup.TryGetValue(propertyName, out var errorList)) { errorList.Clear(); errorList.Add(error!); @@ -114,12 +114,12 @@ namespace ControlCatalog.Models PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - public IEnumerable? GetErrors(string propertyName) + public IEnumerable GetErrors(string? propertyName) { - if (_errorLookup.TryGetValue(propertyName, out List errorList)) + if (propertyName is { } && _errorLookup.TryGetValue(propertyName, out var errorList)) return errorList; else - return null; + return Array.Empty(); } } } diff --git a/samples/ControlCatalog/Pages/AdornerLayerPage.xaml b/samples/ControlCatalog/Pages/AdornerLayerPage.xaml new file mode 100644 index 0000000000..598844d695 --- /dev/null +++ b/samples/ControlCatalog/Pages/AdornerLayerPage.xaml @@ -0,0 +1,66 @@ + + + + + Rotation + + + + + + + + + + + diff --git a/samples/ControlCatalog/Pages/AdornerLayerPage.xaml.cs b/samples/ControlCatalog/Pages/AdornerLayerPage.xaml.cs new file mode 100644 index 0000000000..0cf4a6633c --- /dev/null +++ b/samples/ControlCatalog/Pages/AdornerLayerPage.xaml.cs @@ -0,0 +1,48 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; + +namespace ControlCatalog.Pages +{ + public class AdornerLayerPage : UserControl + { + private Control? _adorner; + + public AdornerLayerPage() + { + this.InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private void RemoveAdorner_OnClick(object? sender, RoutedEventArgs e) + { + var adornerButton = this.FindControl