diff --git a/.editorconfig b/.editorconfig index 9ae52b8bbd..ff7ac5d69e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -64,7 +64,7 @@ dotnet_naming_symbols.static_fields.applicable_kinds = field dotnet_naming_symbols.static_fields.required_modifiers = static dotnet_naming_style.static_prefix_style.required_prefix = s_ -dotnet_naming_style.static_prefix_style.capitalization = camel_case +dotnet_naming_style.static_prefix_style.capitalization = camel_case # internal and private fields should be _camelCase dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion @@ -137,14 +137,36 @@ space_within_single_line_array_initializer_braces = true #Net Analyzer dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomment when all violations are fixed. +# CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = suggestion + +# CS0162: Remove unreachable code +dotnet_diagnostic.CS0162.severity = error +# CA1304: Specify CultureInfo +dotnet_diagnostic.CA1304.severity = warning # CA1802: Use literals where appropriate dotnet_diagnostic.CA1802.severity = warning +# CA1815: Override equals and operator equals on value types +dotnet_diagnostic.CA1815.severity = warning # CA1820: Test for empty strings using string length dotnet_diagnostic.CA1820.severity = warning # CA1821: Remove empty finalizers dotnet_diagnostic.CA1821.severity = warning +# CA1822: Mark members as static +dotnet_diagnostic.CA1822.severity = suggestion +# CA1823: Avoid unused private fields +dotnet_diagnostic.CA1823.severity = warning +dotnet_code_quality.CA1822.api_surface = private, internal # CA1825: Avoid zero-length array allocations dotnet_diagnostic.CA1825.severity = warning +# CA1826: Use property instead of Linq Enumerable method +dotnet_diagnostic.CA1826.severity = suggestion +# CA1827: Do not use Count/LongCount when Any can be used +dotnet_diagnostic.CA1827.severity = warning +# CA1828: Do not use CountAsync/LongCountAsync when AnyAsync can be used +dotnet_diagnostic.CA1828.severity = warning +# CA1829: Use Length/Count property instead of Enumerable.Count method +dotnet_diagnostic.CA1829.severity = warning #CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters dotnet_diagnostic.CA1847.severity = warning diff --git a/.ncrunch/ReactiveUIDemo.v3.ncrunchproject b/.ncrunch/ReactiveUIDemo.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/ReactiveUIDemo.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf index 6ba05332be..3fa8e969c8 100644 --- a/Avalonia.Desktop.slnf +++ b/Avalonia.Desktop.slnf @@ -9,6 +9,7 @@ "samples\\MiniMvvm\\MiniMvvm.csproj", "samples\\SampleControls\\ControlSamples.csproj", "samples\\Sandbox\\Sandbox.csproj", + "samples\\ReactiveUIDemo\\ReactiveUIDemo.csproj", "src\\Avalonia.Base\\Avalonia.Base.csproj", "src\\Avalonia.Build.Tasks\\Avalonia.Build.Tasks.csproj", "src\\Avalonia.Controls.ColorPicker\\Avalonia.Controls.ColorPicker.csproj", diff --git a/Avalonia.sln b/Avalonia.sln index 34b5596119..e6898131b0 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -40,6 +40,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DE .editorconfig = .editorconfig src\Shared\IsExternalInit.cs = src\Shared\IsExternalInit.cs src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs + src\Shared\NullableAttributes.cs = src\Shared\NullableAttributes.cs src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs src\Avalonia.Base\Compatibility\StringCompatibilityExtensions.cs = src\Avalonia.Base\Compatibility\StringCompatibilityExtensions.cs EndProjectSection @@ -119,6 +120,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\SourceLink.props = build\SourceLink.props build\System.Drawing.Common.props = build\System.Drawing.Common.props build\System.Memory.props = build\System.Memory.props + build\TrimmingEnable.props = build\TrimmingEnable.props build\UnitTests.NetFX.props = build\UnitTests.NetFX.props build\XUnit.props = build\XUnit.props EndProjectSection @@ -134,8 +136,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.LinuxFramebuffer", "src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj", "{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Direct3DInteropSample", "samples\interop\Direct3DInteropSample\Direct3DInteropSample.csproj", "{638580B0-7910-40EF-B674-DCB34DA308CD}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.Skia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}" @@ -222,13 +222,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.iOS", "sample EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Desktop", "samples\MobileSandbox.Desktop\MobileSandbox.Desktop.csproj", "{62D392C9-81CF-487F-92E8-598B2AF3FDCE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Browser", "src\Browser\Avalonia.Browser\Avalonia.Browser.csproj", "{4A39637C-9338-4925-A4DB-D072E292EC78}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Browser", "src\Browser\Avalonia.Browser\Avalonia.Browser.csproj", "{4A39637C-9338-4925-A4DB-D072E292EC78}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Browser.Blazor", "src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj", "{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Browser.Blazor", "src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj", "{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Browser.Blazor", "samples\ControlCatalog.Browser.Blazor\ControlCatalog.Browser.Blazor.csproj", "{90B08091-9BBD-4362-B712-E9F2CC62B218}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser.Blazor", "samples\ControlCatalog.Browser.Blazor\ControlCatalog.Browser.Blazor.csproj", "{90B08091-9BBD-4362-B712-E9F2CC62B218}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\ReactiveUIDemo\ReactiveUIDemo.csproj", "{75C47156-C5D8-44BC-A5A7-E8657C2248D6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -362,10 +364,6 @@ Global {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.Build.0 = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.Build.0 = Release|Any CPU {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.Build.0 = Debug|Any CPU {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -541,6 +539,10 @@ Global {90B08091-9BBD-4362-B712-E9F2CC62B218}.Debug|Any CPU.Build.0 = Debug|Any CPU {90B08091-9BBD-4362-B712-E9F2CC62B218}.Release|Any CPU.ActiveCfg = Release|Any CPU {90B08091-9BBD-4362-B712-E9F2CC62B218}.Release|Any CPU.Build.0 = Release|Any CPU + {75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -572,7 +574,6 @@ Global {7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E} {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098} {854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} - {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {E1582370-37B3-403C-917F-8209551B1634} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {E2999E4A-9086-401F-898C-AEB0AD38E676} = {9B9E3891-2366-4253-A952-D08BCEB71098} @@ -605,6 +606,7 @@ Global {47F8530C-F19B-4B1A-B4D6-EB231522AE5D} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268} {15B93A4C-1B46-43F6-B534-7B25B6E99932} = {9B9E3891-2366-4253-A952-D08BCEB71098} {90B08091-9BBD-4362-B712-E9F2CC62B218} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/build/SharedVersion.props b/build/SharedVersion.props index 5838519596..e9c3d65b41 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -7,7 +7,7 @@ https://avaloniaui.net https://github.com/AvaloniaUI/Avalonia/ true - CS1591 + $(NoWarn);CS1591 preview MIT Icon.png diff --git a/build/TrimmingEnable.props b/build/TrimmingEnable.props new file mode 100644 index 0000000000..3f873e3345 --- /dev/null +++ b/build/TrimmingEnable.props @@ -0,0 +1,16 @@ + + + false + true + false + true + + + + true + + $(WarningsAsErrors);IL2000;IL2001;IL2002;IL2003;IL2004;IL2005;IL2006;IL2007;IL2008;IL2009;IL2010;IL2011;IL2012;IL2013;IL2014;IL2015;IL2016;IL2017;IL2018;IL2019;IL2020;IL2021;IL2022;IL2023;IL2024;IL2025;IL2026;IL2027;IL2028;IL2029;IL2030;IL2031;IL2032;IL2033;IL2034;IL2035;IL2036;IL2037;IL2038;IL2039;IL2040;IL2041;IL2042;IL2043;IL2044;IL2045;IL2046;IL2047;IL2048;IL2049;IL2050;IL2051;IL2052;IL2053;IL2054;IL2055;IL2056;IL2057;IL2058;IL2059;IL2060;IL2061;IL2062;IL2063;IL2064;IL2065;IL2066;IL2067;IL2068;IL2069;IL2070;IL2071;IL2072;IL2073;IL2074;IL2075;IL2076;IL2077;IL2078;IL2079;IL2080;IL2081;IL2082;IL2083;IL2084;IL2085;IL2086;IL2087;IL2088;IL2089;IL2090;IL2091;IL2092;IL2093;IL2094;IL2095;IL2096;IL2097;IL2098;IL2099;IL2100;IL2101;IL2102;IL2103;IL2104;IL2105;IL2106;IL2107;IL2108;IL2109;IL2110;IL2111;IL2112;IL2113;IL2114;IL2115;IL2116;IL2117;IL2118;IL2119;IL2120;IL2121;IL2122;IL2123;IL2124;IL2125;IL2126;IL2127;IL2128;IL2129;IL2130;IL2131;IL2132;IL2133;IL2134;IL2135;IL2136;IL2137;IL2138;IL2139;IL2140;IL2141;IL2142;IL2143;IL2144;IL2145;IL2146;IL2147;IL2148;IL2149;IL2150;IL2151;IL2152;IL2153;IL2154;IL2155;IL2156;IL2157 + + $(WarningsAsErrors);IL3050;IL3051;IL3052;IL3053;IL3054;IL3055;IL3056 + + diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 2443965957..b6dacb6ce4 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -137,7 +137,11 @@ void WindowImpl::BringToFront() for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) { - (*iterator)->BringToFront(); + auto window = (*iterator)->Window; + + // #9565: Only bring window to front if it's on the currently active space + if ([window isOnActiveSpace]) + (*iterator)->BringToFront(); } } } @@ -161,6 +165,9 @@ void WindowImpl::StartStateTransition() { void WindowImpl::EndStateTransition() { _transitioningWindowState = false; + + // Ensure correct order of child windows after fullscreen transition. + BringToFront(); } SystemDecorations WindowImpl::Decorations() { diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 2295c0beda..3704cee890 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -80,7 +80,6 @@ partial class Build : NukeBuild if (Parameters.IsRunningOnAzure) c.AddProperty("JavaSdkDirectory", GetVariable("JAVA_HOME_11_X64")); c.AddProperty("PackageVersion", Parameters.Version) - .AddProperty("iOSRoslynPathHackRequired", true) .SetConfiguration(Parameters.Configuration) .SetVerbosity(DotNetVerbosity.Minimal); return c; diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 92d9732e91..13bac4b7db 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -4,7 +4,7 @@ false False - CS0649;CS0169;SYSLIB0011 + $(NoWarn);CS0649;CS0169;SYSLIB0011 1 net7.0 diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 1b822c14cf..ca2d4b66ed 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -30,7 +30,9 @@ /> - + $(IntermediateOutputPath)/Avalonia/resources @@ -62,7 +64,9 @@ BeforeTargets="CoreCompile;CoreResGen" Inputs="@(AvaloniaResource);@(AvaloniaXaml);@(CustomAdditionalGenerateAvaloniaResourcesInputs);$(MSBuildAllProjects)" Outputs="$(AvaloniaResourcesTemporaryFilePath)" - DependsOnTargets="$(BuildAvaloniaResourcesDependsOn)"> + DependsOnTargets="$(BuildAvaloniaResourcesDependsOn)" + Condition="('@(AvaloniaResource->Count())' > 0) or ('@(AvaloniaXaml->Count())' > 0)" + > @@ -81,7 +85,12 @@ $(IntermediateOutputPath)/Avalonia/references diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index b1bacc6483..d5e5cb14dc 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -99,6 +99,15 @@ namespace ControlCatalog.NetCore SilenceConsole(); return builder.StartLinuxDrm(args, scaling: GetScaling()); } + else if (args.Contains("--dxgi")) + { + builder.With(new Win32PlatformOptions() + { + UseLowLatencyDxgiSwapChain = true, + UseWindowsUIComposition = false + }); + return builder.StartWithClassicDesktopLifetime(args); + } else return builder.StartWithClassicDesktopLifetime(args); } diff --git a/samples/ControlCatalog.NetCore/Properties/launchSettings.json b/samples/ControlCatalog.NetCore/Properties/launchSettings.json new file mode 100644 index 0000000000..5964ca320e --- /dev/null +++ b/samples/ControlCatalog.NetCore/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "ControlCatalog.NetCore": { + "commandName": "Project" + }, + "Dxgi": { + "commandName": "Project", + "commandLineArgs": "--dxgi" + } + } +} \ No newline at end of file diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 6b550a30be..18f0dd16ba 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -33,4 +33,14 @@ + + + + + + + + + + diff --git a/samples/ControlCatalog/Converter/DegToRadConverter.cs b/samples/ControlCatalog/Converter/DegToRadConverter.cs new file mode 100644 index 0000000000..b062bcb64a --- /dev/null +++ b/samples/ControlCatalog/Converter/DegToRadConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using Avalonia.Data.Converters; + +namespace ControlCatalog.Converter +{ + public class DegToRadConverter : IValueConverter + { + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is double rad) + { + return rad * 180.0d / Math.PI; + } + return 0.0d; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is double deg) + { + return deg / 180.0d * Math.PI; + } + return 0.0d; + } + } +} diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index b5a09b5fbd..4a5f5bc96c 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -66,6 +66,9 @@ + + + @@ -132,6 +135,9 @@ + + + diff --git a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs index 8d9cd74ea7..ae7e43f511 100644 --- a/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs @@ -14,7 +14,26 @@ namespace ControlCatalog.Pages { public class AutoCompleteBoxPage : UserControl { - private StateData[] BuildAllStates() + public class StateData + { + public string Name { get; private set; } + public string Abbreviation { get; private set; } + public string Capital { get; private set; } + + public StateData(string name, string abbreviatoin, string capital) + { + Name = name; + Abbreviation = abbreviatoin; + Capital = capital; + } + + public override string ToString() + { + return Name; + } + } + + private static StateData[] BuildAllStates() { return new StateData[] { @@ -72,7 +91,7 @@ namespace ControlCatalog.Pages } public StateData[] States { get; private set; } - private LinkedList[] BuildAllSentences() + private static LinkedList[] BuildAllSentences() { return new string[] { @@ -124,7 +143,7 @@ namespace ControlCatalog.Pages .OfType(); } - private bool StringContains(string str, string? query) + private static bool StringContains(string str, string? query) { if (query == null) return false; return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0; diff --git a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs index 61e0ed5acb..c70675b606 100644 --- a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs +++ b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs @@ -25,7 +25,7 @@ public partial class CompositionPage : UserControl this.Get("Items").Items = CreateColorItems(); } - private List CreateColorItems() + private static List CreateColorItems() { var list = new List(); diff --git a/samples/ControlCatalog/Pages/CustomDrawing.xaml b/samples/ControlCatalog/Pages/CustomDrawing.xaml new file mode 100644 index 0000000000..04b7fcfea5 --- /dev/null +++ b/samples/ControlCatalog/Pages/CustomDrawing.xaml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/IntegrationTestApp/MainWindow.axaml.cs b/samples/IntegrationTestApp/MainWindow.axaml.cs index c1acc7ca88..841947673a 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml.cs +++ b/samples/IntegrationTestApp/MainWindow.axaml.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Avalonia; @@ -178,6 +179,8 @@ namespace IntegrationTestApp ShowWindow(); if (source?.Name == "SendToBack") SendToBack(); + if (source?.Name == "EnterFullscreen") + WindowState = WindowState.FullScreen; if (source?.Name == "ExitFullscreen") WindowState = WindowState.Normal; if (source?.Name == "RestoreAll") diff --git a/samples/IntegrationTestApp/ShowWindowTest.axaml b/samples/IntegrationTestApp/ShowWindowTest.axaml index c3a0d8d2e2..00987429d0 100644 --- a/samples/IntegrationTestApp/ShowWindowTest.axaml +++ b/samples/IntegrationTestApp/ShowWindowTest.axaml @@ -27,10 +27,10 @@ - Normal - Minimized - Maximized - FullScreen + Normal + Minimized + Maximized + FullScreen diff --git a/samples/ReactiveUIDemo/App.axaml b/samples/ReactiveUIDemo/App.axaml new file mode 100644 index 0000000000..dd3a39f6ac --- /dev/null +++ b/samples/ReactiveUIDemo/App.axaml @@ -0,0 +1,8 @@ + + + + + diff --git a/samples/ReactiveUIDemo/App.axaml.cs b/samples/ReactiveUIDemo/App.axaml.cs new file mode 100644 index 0000000000..4578566427 --- /dev/null +++ b/samples/ReactiveUIDemo/App.axaml.cs @@ -0,0 +1,37 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; +using ReactiveUI; +using ReactiveUIDemo.ViewModels; +using ReactiveUIDemo.Views; +using Splat; + +namespace ReactiveUIDemo +{ + public class App : Application + { + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + Locator.CurrentMutable.Register(() => new FooView(), typeof(IViewFor)); + Locator.CurrentMutable.Register(() => new BarView(), typeof(IViewFor)); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow(); + base.OnFrameworkInitializationCompleted(); + } + + public static int Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .UseReactiveUI() + .LogToTrace(); + } +} diff --git a/samples/ReactiveUIDemo/MainWindow.axaml b/samples/ReactiveUIDemo/MainWindow.axaml new file mode 100644 index 0000000000..7775fc5a79 --- /dev/null +++ b/samples/ReactiveUIDemo/MainWindow.axaml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/samples/ReactiveUIDemo/MainWindow.axaml.cs b/samples/ReactiveUIDemo/MainWindow.axaml.cs new file mode 100644 index 0000000000..5bf2d476fd --- /dev/null +++ b/samples/ReactiveUIDemo/MainWindow.axaml.cs @@ -0,0 +1,22 @@ +using ReactiveUIDemo.ViewModels; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace ReactiveUIDemo +{ + public class MainWindow : Window + { + public MainWindow() + { + this.InitializeComponent(); + this.DataContext = new MainWindowViewModel(); + this.AttachDevTools(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/samples/ReactiveUIDemo/ReactiveUIDemo.csproj b/samples/ReactiveUIDemo/ReactiveUIDemo.csproj new file mode 100644 index 0000000000..94ca4ee809 --- /dev/null +++ b/samples/ReactiveUIDemo/ReactiveUIDemo.csproj @@ -0,0 +1,28 @@ + + + Exe + net6.0 + enable + + + + + + + + + + + BarView.axaml + + + FooView.axaml + + + + + + + + + diff --git a/samples/ReactiveUIDemo/ViewModels/BarViewModel.cs b/samples/ReactiveUIDemo/ViewModels/BarViewModel.cs new file mode 100644 index 0000000000..3448453d81 --- /dev/null +++ b/samples/ReactiveUIDemo/ViewModels/BarViewModel.cs @@ -0,0 +1,11 @@ +using ReactiveUI; + +namespace ReactiveUIDemo.ViewModels +{ + internal class BarViewModel : ReactiveObject, IRoutableViewModel + { + public BarViewModel(IScreen screen) => HostScreen = screen; + public string UrlPathSegment => "Bar"; + public IScreen HostScreen { get; } + } +} diff --git a/samples/ReactiveUIDemo/ViewModels/FooViewModel.cs b/samples/ReactiveUIDemo/ViewModels/FooViewModel.cs new file mode 100644 index 0000000000..1a363e18dc --- /dev/null +++ b/samples/ReactiveUIDemo/ViewModels/FooViewModel.cs @@ -0,0 +1,11 @@ +using ReactiveUI; + +namespace ReactiveUIDemo.ViewModels +{ + internal class FooViewModel : ReactiveObject, IRoutableViewModel + { + public FooViewModel(IScreen screen) => HostScreen = screen; + public string UrlPathSegment => "Foo"; + public IScreen HostScreen { get; } + } +} diff --git a/samples/ReactiveUIDemo/ViewModels/MainWindowViewModel.cs b/samples/ReactiveUIDemo/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000000..2222137d38 --- /dev/null +++ b/samples/ReactiveUIDemo/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,9 @@ +using ReactiveUI; + +namespace ReactiveUIDemo.ViewModels +{ + internal class MainWindowViewModel : ReactiveObject + { + public RoutedViewHostPageViewModel RoutedViewHost { get; } = new(); + } +} diff --git a/samples/ReactiveUIDemo/ViewModels/RoutedViewHostPageViewModel.cs b/samples/ReactiveUIDemo/ViewModels/RoutedViewHostPageViewModel.cs new file mode 100644 index 0000000000..701447cfe8 --- /dev/null +++ b/samples/ReactiveUIDemo/ViewModels/RoutedViewHostPageViewModel.cs @@ -0,0 +1,21 @@ +using ReactiveUI; + +namespace ReactiveUIDemo.ViewModels +{ + internal class RoutedViewHostPageViewModel : ReactiveObject, IScreen + { + public RoutedViewHostPageViewModel() + { + Foo = new(this); + Bar = new(this); + Router.Navigate.Execute(Foo); + } + + public RoutingState Router { get; } = new(); + public FooViewModel Foo { get; } + public BarViewModel Bar { get; } + + public void ShowFoo() => Router.Navigate.Execute(Foo); + public void ShowBar() => Router.Navigate.Execute(Bar); + } +} diff --git a/samples/ReactiveUIDemo/Views/BarView.axaml b/samples/ReactiveUIDemo/Views/BarView.axaml new file mode 100644 index 0000000000..2622245997 --- /dev/null +++ b/samples/ReactiveUIDemo/Views/BarView.axaml @@ -0,0 +1,16 @@ + + + + Bar! + + + diff --git a/samples/ReactiveUIDemo/Views/BarView.axaml.cs b/samples/ReactiveUIDemo/Views/BarView.axaml.cs new file mode 100644 index 0000000000..2fbea6de91 --- /dev/null +++ b/samples/ReactiveUIDemo/Views/BarView.axaml.cs @@ -0,0 +1,28 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using ReactiveUI; +using ReactiveUIDemo.ViewModels; + +namespace ReactiveUIDemo.Views +{ + internal partial class BarView : UserControl, IViewFor + { + public BarView() + { + InitializeComponent(); + } + + public BarViewModel? ViewModel { get; set; } + + object? IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (BarViewModel?)value; + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/samples/ReactiveUIDemo/Views/FooView.axaml b/samples/ReactiveUIDemo/Views/FooView.axaml new file mode 100644 index 0000000000..8f73250d3b --- /dev/null +++ b/samples/ReactiveUIDemo/Views/FooView.axaml @@ -0,0 +1,16 @@ + + + + Foo! + + + diff --git a/samples/ReactiveUIDemo/Views/FooView.axaml.cs b/samples/ReactiveUIDemo/Views/FooView.axaml.cs new file mode 100644 index 0000000000..313a71044c --- /dev/null +++ b/samples/ReactiveUIDemo/Views/FooView.axaml.cs @@ -0,0 +1,28 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using ReactiveUI; +using ReactiveUIDemo.ViewModels; + +namespace ReactiveUIDemo.Views +{ + internal partial class FooView : UserControl, IViewFor + { + public FooView() + { + InitializeComponent(); + } + + public FooViewModel? ViewModel { get; set; } + + object? IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (FooViewModel?)value; + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/samples/RenderDemo/Pages/TextFormatterPage.axaml.cs b/samples/RenderDemo/Pages/TextFormatterPage.axaml.cs index 57a5c7101f..8fbfa854b1 100644 --- a/samples/RenderDemo/Pages/TextFormatterPage.axaml.cs +++ b/samples/RenderDemo/Pages/TextFormatterPage.axaml.cs @@ -90,7 +90,7 @@ namespace RenderDemo.Pages return new ControlRun(_control, _defaultProperties); } - return new TextCharacters(_text.AsMemory(), _defaultProperties); + return new TextCharacters(_text, _defaultProperties); } } diff --git a/samples/interop/Direct3DInteropSample/App.paml b/samples/interop/Direct3DInteropSample/App.paml deleted file mode 100644 index e6d77dfaf4..0000000000 --- a/samples/interop/Direct3DInteropSample/App.paml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/samples/interop/Direct3DInteropSample/App.paml.cs b/samples/interop/Direct3DInteropSample/App.paml.cs deleted file mode 100644 index 29365decfe..0000000000 --- a/samples/interop/Direct3DInteropSample/App.paml.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; - -namespace Direct3DInteropSample -{ - public class App : Application - { - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - desktop.MainWindow = new MainWindow(); - base.OnFrameworkInitializationCompleted(); - } - } -} diff --git a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj deleted file mode 100644 index f9ef4693d5..0000000000 --- a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - Exe - net461 - - - - - - %(Filename) - - - Designer - - - - - - - - PreserveNewest - - - - - - - - - - - diff --git a/samples/interop/Direct3DInteropSample/MainWindow.cs b/samples/interop/Direct3DInteropSample/MainWindow.cs deleted file mode 100644 index 6cc3cb9116..0000000000 --- a/samples/interop/Direct3DInteropSample/MainWindow.cs +++ /dev/null @@ -1,283 +0,0 @@ -using System; - -using Avalonia; -using Avalonia.Controls; -using Avalonia.Direct2D1; -using Avalonia.Direct2D1.Media; -using Avalonia.Markup.Xaml; -using Avalonia.Platform; -using Avalonia.Rendering; - -using SharpDX; -using SharpDX.D3DCompiler; -using SharpDX.Direct2D1; -using SharpDX.Direct3D; -using SharpDX.Direct3D11; -using SharpDX.DXGI; - -using AlphaMode = SharpDX.Direct2D1.AlphaMode; -using Buffer = SharpDX.Direct3D11.Buffer; -using DeviceContext = SharpDX.Direct2D1.DeviceContext; -using Factory2 = SharpDX.DXGI.Factory2; -using InputElement = SharpDX.Direct3D11.InputElement; -using Matrix = SharpDX.Matrix; -using PixelFormat = SharpDX.Direct2D1.PixelFormat; -using Resource = SharpDX.Direct3D11.Resource; - -namespace Direct3DInteropSample -{ - public class MainWindow : Window - { - Texture2D _backBuffer; - RenderTargetView _renderView; - Texture2D _depthBuffer; - DepthStencilView _depthView; - private readonly SwapChain _swapChain; - private SwapChainDescription1 _desc; - private Matrix _proj = Matrix.Identity; - private readonly Matrix _view; - private Buffer _contantBuffer; - private DeviceContext _deviceContext; - private readonly MainWindowViewModel _model; - - public MainWindow() - { - DataContext = _model = new MainWindowViewModel(); - - _desc = new SwapChainDescription1() - { - BufferCount = 1, - Width = (int)ClientSize.Width, - Height = (int)ClientSize.Height, - Format = Format.R8G8B8A8_UNorm, - SampleDescription = new SampleDescription(1, 0), - SwapEffect = SwapEffect.Discard, - Usage = Usage.RenderTargetOutput - }; - - using (var factory = Direct2D1Platform.DxgiDevice.Adapter.GetParent()) - { - _swapChain = new SwapChain1(factory, Direct2D1Platform.DxgiDevice, PlatformImpl?.Handle.Handle ?? IntPtr.Zero, ref _desc); - } - - _deviceContext = new DeviceContext(Direct2D1Platform.Direct2D1Device, DeviceContextOptions.None) - { - DotsPerInch = new Size2F(96, 96) - }; - - CreateMesh(); - - _view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY); - - this.GetObservable(ClientSizeProperty).Subscribe(Resize); - - Resize(ClientSize); - - AvaloniaXamlLoader.Load(this); - - Background = Avalonia.Media.Brushes.Transparent; - } - - - protected override void HandlePaint(Rect rect) - { - var viewProj = Matrix.Multiply(_view, _proj); - var context = Direct2D1Platform.Direct3D11Device.ImmediateContext; - - // Clear views - context.ClearDepthStencilView(_depthView, DepthStencilClearFlags.Depth, 1.0f, 0); - context.ClearRenderTargetView(_renderView, Color.White); - - // Update WorldViewProj Matrix - var worldViewProj = Matrix.RotationX((float)_model.RotationX) * Matrix.RotationY((float)_model.RotationY) - * Matrix.RotationZ((float)_model.RotationZ) - * Matrix.Scaling((float)_model.Zoom) - * viewProj; - worldViewProj.Transpose(); - context.UpdateSubresource(ref worldViewProj, _contantBuffer); - - // Draw the cube - context.Draw(36, 0); - base.HandlePaint(rect); - - // Present! - _swapChain.Present(0, PresentFlags.None); - } - - private void CreateMesh() - { - var device = Direct2D1Platform.Direct3D11Device; - - // Compile Vertex and Pixel shaders - var vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "VS", "vs_4_0"); - var vertexShader = new VertexShader(device, vertexShaderByteCode); - - var pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "PS", "ps_4_0"); - var pixelShader = new PixelShader(device, pixelShaderByteCode); - - var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode); - - var inputElements = new[] - { - new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), - new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0) - }; - - // Layout from VertexShader input signature - var layout = new InputLayout( - device, - signature, - inputElements); - - // Instantiate Vertex buffer from vertex data - var vertices = Buffer.Create( - device, - BindFlags.VertexBuffer, - new[] - { - new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front - new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), - new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), - new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), - new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), - new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), - - new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // BACK - new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), - new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), - new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), - new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), - new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), - - new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top - new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), - new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), - new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), - new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), - new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), - - new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom - new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), - new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), - new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), - new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), - new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), - - new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left - new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), - new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), - new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), - new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), - new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), - - new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right - new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), - new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), - new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), - new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), - new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), - }); - - // Create Constant Buffer - _contantBuffer = new Buffer(device, Utilities.SizeOf(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); - - var context = Direct2D1Platform.Direct3D11Device.ImmediateContext; - - // Prepare All the stages - context.InputAssembler.InputLayout = layout; - context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; - context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, Utilities.SizeOf() * 2, 0)); - context.VertexShader.SetConstantBuffer(0, _contantBuffer); - context.VertexShader.Set(vertexShader); - context.PixelShader.Set(pixelShader); - } - - private void Resize(Size size) - { - Utilities.Dispose(ref _deviceContext); - Utilities.Dispose(ref _backBuffer); - Utilities.Dispose(ref _renderView); - Utilities.Dispose(ref _depthBuffer); - Utilities.Dispose(ref _depthView); - var context = Direct2D1Platform.Direct3D11Device.ImmediateContext; - - // Resize the backbuffer - _swapChain.ResizeBuffers(0, 0, 0, Format.Unknown, SwapChainFlags.None); - - // Get the backbuffer from the swapchain - _backBuffer = Resource.FromSwapChain(_swapChain, 0); - - // Renderview on the backbuffer - _renderView = new RenderTargetView(Direct2D1Platform.Direct3D11Device, _backBuffer); - - // Create the depth buffer - _depthBuffer = new Texture2D( - Direct2D1Platform.Direct3D11Device, - new Texture2DDescription() - { - Format = Format.D32_Float_S8X24_UInt, - ArraySize = 1, - MipLevels = 1, - Width = (int)size.Width, - Height = (int)size.Height, - SampleDescription = new SampleDescription(1, 0), - Usage = ResourceUsage.Default, - BindFlags = BindFlags.DepthStencil, - CpuAccessFlags = CpuAccessFlags.None, - OptionFlags = ResourceOptionFlags.None - }); - - // Create the depth buffer view - _depthView = new DepthStencilView(Direct2D1Platform.Direct3D11Device, _depthBuffer); - - // Setup targets and viewport for rendering - context.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f)); - context.OutputMerger.SetTargets(_depthView, _renderView); - - // Setup new projection matrix with correct aspect ratio - _proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f); - - using (var dxgiBackBuffer = _swapChain.GetBackBuffer(0)) - { - var renderTarget = new SharpDX.Direct2D1.RenderTarget( - Direct2D1Platform.Direct2D1Factory, - dxgiBackBuffer, - new RenderTargetProperties - { - DpiX = 96, - DpiY = 96, - Type = RenderTargetType.Default, - PixelFormat = new PixelFormat( - Format.Unknown, - AlphaMode.Premultiplied) - }); - - _deviceContext = renderTarget.QueryInterface(); - - renderTarget.Dispose(); - } - } - - private class D3DRenderTarget : IRenderTarget - { - private readonly MainWindow _window; - - public D3DRenderTarget(MainWindow window) - { - _window = window; - } - - public void Dispose() - { - } - - public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) - { - return new DrawingContextImpl(visualBrushRenderer, null, _window._deviceContext); - } - } - - - protected override IRenderTarget CreateRenderTarget() => new D3DRenderTarget(this); - } -} diff --git a/samples/interop/Direct3DInteropSample/MainWindow.paml b/samples/interop/Direct3DInteropSample/MainWindow.paml deleted file mode 100644 index 37c6265836..0000000000 --- a/samples/interop/Direct3DInteropSample/MainWindow.paml +++ /dev/null @@ -1,14 +0,0 @@ - - - - Rotation X - - Rotation Y - - Rotation Z - - Zoom - - - - \ No newline at end of file diff --git a/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs b/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs deleted file mode 100644 index 21679a99c5..0000000000 --- a/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MiniMvvm; - -namespace Direct3DInteropSample -{ - public class MainWindowViewModel : ViewModelBase - { - private double _rotationX; - - public double RotationX - { - get { return _rotationX; } - set { this.RaiseAndSetIfChanged(ref _rotationX, value); } - } - - private double _rotationY = 1; - - public double RotationY - { - get { return _rotationY; } - set { this.RaiseAndSetIfChanged(ref _rotationY, value); } - } - - private double _rotationZ = 2; - - public double RotationZ - { - get { return _rotationZ; } - set { this.RaiseAndSetIfChanged(ref _rotationZ, value); } - } - - - private double _zoom = 1; - - public double Zoom - { - get { return _zoom; } - set { this.RaiseAndSetIfChanged(ref _zoom, value); } - } - } -} diff --git a/samples/interop/Direct3DInteropSample/MiniCube.fx b/samples/interop/Direct3DInteropSample/MiniCube.fx deleted file mode 100644 index f246421f2d..0000000000 --- a/samples/interop/Direct3DInteropSample/MiniCube.fx +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -struct VS_IN -{ - float4 pos : POSITION; - float4 col : COLOR; -}; - -struct PS_IN -{ - float4 pos : SV_POSITION; - float4 col : COLOR; -}; - -float4x4 worldViewProj; - -PS_IN VS( VS_IN input ) -{ - PS_IN output = (PS_IN)0; - - output.pos = mul(input.pos, worldViewProj); - output.col = input.col; - - return output; -} - -float4 PS( PS_IN input ) : SV_Target -{ - return input.col; -} \ No newline at end of file diff --git a/samples/interop/Direct3DInteropSample/Program.cs b/samples/interop/Direct3DInteropSample/Program.cs deleted file mode 100644 index bf8e76d7e4..0000000000 --- a/samples/interop/Direct3DInteropSample/Program.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Avalonia; - -namespace Direct3DInteropSample -{ - class Program - { - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .With(new Win32PlatformOptions { UseDeferredRendering = false }) - .UseWin32() - .UseDirect2D1(); - - public static int Main(string[] args) - => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - } -} diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index 8d56086470..c885a7768c 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -167,7 +167,7 @@ namespace Avalonia.Android } } - public readonly struct ComposingRegion + public readonly record struct ComposingRegion { private readonly int _start = -1; private readonly int _end = -1; diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index 2b6d29e7c5..75856e4b52 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -32,6 +32,7 @@ namespace Avalonia.Android public static AndroidPlatformOptions Options { get; private set; } internal static Compositor Compositor { get; private set; } + internal static PlatformRenderInterfaceContextManager RenderInterface { get; private set; } public static void Initialize() { @@ -51,15 +52,19 @@ namespace Avalonia.Android if (Options.UseGpu) { - EglPlatformOpenGlInterface.TryInitialize(); + EglPlatformGraphics.TryInitialize(); } if (Options.UseCompositor) { Compositor = new Compositor( AvaloniaLocator.Current.GetRequiredService(), - AvaloniaLocator.Current.GetService()); + AvaloniaLocator.Current.GetService()); } + else + RenderInterface = + new PlatformRenderInterfaceContextManager(AvaloniaLocator.Current + .GetService()); } } diff --git a/src/Android/Avalonia.Android/Avalonia.Android.csproj b/src/Android/Avalonia.Android/Avalonia.Android.csproj index 6688dde8f5..66557418dd 100644 --- a/src/Android/Avalonia.Android/Avalonia.Android.csproj +++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj @@ -16,4 +16,5 @@ + diff --git a/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs b/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs deleted file mode 100644 index e85ed11028..0000000000 --- a/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Avalonia.OpenGL; -using Avalonia.OpenGL.Egl; -using Avalonia.OpenGL.Surfaces; - -namespace Avalonia.Android.OpenGL -{ - internal sealed class GlPlatformSurface : EglGlPlatformSurfaceBase - { - private readonly EglPlatformOpenGlInterface _egl; - private readonly IEglWindowGlPlatformSurfaceInfo _info; - - private GlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) - { - _egl = egl; - _info = info; - } - - public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() => - new GlRenderTarget(_egl, _info, _egl.CreateWindowSurface(_info.Handle), _info.Handle); - - public static GlPlatformSurface TryCreate(IEglWindowGlPlatformSurfaceInfo info) - { - var feature = AvaloniaLocator.Current.GetService(); - if (feature is EglPlatformOpenGlInterface egl) - { - return new GlPlatformSurface(egl, info); - } - - return null; - } - } -} diff --git a/src/Android/Avalonia.Android/OpenGL/GlRenderTarget.cs b/src/Android/Avalonia.Android/OpenGL/GlRenderTarget.cs deleted file mode 100644 index f9071d9b27..0000000000 --- a/src/Android/Avalonia.Android/OpenGL/GlRenderTarget.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; - -using Avalonia.OpenGL.Egl; -using Avalonia.OpenGL.Surfaces; - -namespace Avalonia.Android.OpenGL -{ - internal sealed class GlRenderTarget : EglPlatformSurfaceRenderTargetBase, IGlPlatformSurfaceRenderTargetWithCorruptionInfo - { - private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info; - private readonly EglSurface _surface; - private readonly IntPtr _handle; - - public GlRenderTarget( - EglPlatformOpenGlInterface egl, - EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, - EglSurface surface, - IntPtr handle) - : base(egl) - { - _info = info; - _surface = surface; - _handle = handle; - } - - public bool IsCorrupted => _handle != _info.Handle; - - public override IGlPlatformSurfaceRenderingSession BeginDraw() => BeginDraw(_surface, _info); - } -} diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs index aabf8160f8..94e5f4bd01 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs @@ -96,12 +96,14 @@ namespace Avalonia.Android.Platform.SkiaPlatform public IntPtr bits; // Do not touch. +#pragma warning disable CA1823 // Avoid unused private fields uint reserved1; uint reserved2; uint reserved3; uint reserved4; uint reserved5; uint reserved6; +#pragma warning restore CA1823 // Avoid unused private fields } } } diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 4150b52946..56dbadca03 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -8,7 +8,6 @@ using Android.Runtime; using Android.Text; using Android.Views; using Android.Views.InputMethods; -using Avalonia.Android.OpenGL; using Avalonia.Android.Platform.Specific; using Avalonia.Android.Platform.Specific.Helpers; using Avalonia.Android.Platform.Storage; @@ -30,7 +29,7 @@ using AndroidRect = Android.Graphics.Rect; namespace Avalonia.Android.Platform.SkiaPlatform { - class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo, + class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo, ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost, ITopLevelImplWithStorageProvider { private readonly IGlPlatformSurface _gl; @@ -47,7 +46,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform _textInputMethod = new AndroidInputMethod(_view); _keyboardHelper = new AndroidKeyboardEventsHelper(this); _pointerHelper = new AndroidMotionEventsHelper(this); - _gl = GlPlatformSurface.TryCreate(this); + _gl = new EglGlPlatformSurface(this); _framebuffer = new FramebufferManager(this); RenderScaling = _view.Scaling; @@ -106,10 +105,15 @@ namespace Avalonia.Android.Platform.SkiaPlatform public IRenderer CreateRenderer(IRenderRoot root) => AndroidPlatform.Options.UseCompositor - ? new CompositingRenderer(root, AndroidPlatform.Compositor) + ? new CompositingRenderer(root, AndroidPlatform.Compositor, () => Surfaces) : AndroidPlatform.Options.UseDeferredRendering - ? new DeferredRenderer(root, AvaloniaLocator.Current.GetRequiredService()) { RenderOnlyOnRenderThread = true } - : new ImmediateRenderer((Visual)root); + ? new DeferredRenderer(root, AvaloniaLocator.Current.GetRequiredService(), + () => AndroidPlatform.RenderInterface.CreateRenderTarget(Surfaces), + AndroidPlatform.RenderInterface) + { RenderOnlyOnRenderThread = true } + : new ImmediateRenderer((Visual)root, + () => AndroidPlatform.RenderInterface.CreateRenderTarget(Surfaces), + AndroidPlatform.RenderInterface); public virtual void Hide() { @@ -283,7 +287,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1); - IntPtr EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo.Handle => ((IPlatformHandle)_view).Handle; + IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => ((IPlatformHandle)_view).Handle; public PixelSize Size => _view.Size; diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs index 57f6469a42..bc24a16e56 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs @@ -30,7 +30,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers return DispatchKeyEventInternal(e, out callBase); } - string UnicodeTextInput(KeyEvent keyEvent) + static string UnicodeTextInput(KeyEvent keyEvent) { return keyEvent.Action == KeyEventActions.Multiple && keyEvent.RepeatCount == 0 diff --git a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs index a9b2e16d43..078f70db60 100644 --- a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs +++ b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs @@ -178,7 +178,7 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF return false; } - private Stream? GetVirtualFileStream(Context context, AndroidUri uri, bool isOutput) + private static Stream? GetVirtualFileStream(Context context, AndroidUri uri, bool isOutput) { var mimeTypes = context.ContentResolver?.GetStreamTypes(uri, FilePickerFileTypes.All.MimeTypes![0]); if (mimeTypes?.Length >= 1) diff --git a/src/Avalonia.Base/Animation/Animation.cs b/src/Avalonia.Base/Animation/Animation.cs index e0883901fd..06087cdd6a 100644 --- a/src/Avalonia.Base/Animation/Animation.cs +++ b/src/Avalonia.Base/Animation/Animation.cs @@ -202,9 +202,7 @@ namespace Avalonia.Animation /// The animation setter. /// The property animator value. public static void SetAnimator(IAnimationSetter setter, -#if NET6_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods)] -#endif Type value) { s_animators[setter] = (value, () => (IAnimator)Activator.CreateInstance(value)!); diff --git a/src/Avalonia.Base/Animation/AnimatorKeyFrame.cs b/src/Avalonia.Base/Animation/AnimatorKeyFrame.cs index 0356723f00..3168a67d79 100644 --- a/src/Avalonia.Base/Animation/AnimatorKeyFrame.cs +++ b/src/Avalonia.Base/Animation/AnimatorKeyFrame.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using Avalonia.Animation.Animators; using Avalonia.Data; using Avalonia.Reactive; @@ -66,7 +67,8 @@ namespace Avalonia.Animation } } - public T GetTypedValue() + [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)] + public T GetTypedValue<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>() { var typeConv = TypeDescriptor.GetConverter(typeof(T)); diff --git a/src/Avalonia.Base/Animation/Animators/Animator`1.cs b/src/Avalonia.Base/Animation/Animators/Animator`1.cs index 8765cfb4c9..b5d1feb4a7 100644 --- a/src/Avalonia.Base/Animation/Animators/Animator`1.cs +++ b/src/Avalonia.Base/Animation/Animators/Animator`1.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Disposables; using System.Reactive.Linq; using Avalonia.Animation.Utils; using Avalonia.Collections; @@ -39,7 +40,7 @@ namespace Avalonia.Animation.Animators VerifyConvertKeyFrames(); var subject = new DisposeAnimationInstanceSubject(this, animation, control, clock, onComplete); - return match.Subscribe(subject); + return new CompositeDisposable(match.Subscribe(subject), subject); } protected T InterpolationHandler(double animationTime, T neutralValue) diff --git a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs index 4727ea1bfb..068c190fa1 100644 --- a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs @@ -72,7 +72,7 @@ namespace Avalonia.Animation.Animators return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation); } - private IReadOnlyList InterpolateStops(double progress, IReadOnlyList oldValue, IReadOnlyList newValue) + private static IReadOnlyList InterpolateStops(double progress, IReadOnlyList oldValue, IReadOnlyList newValue) { var resultCount = Math.Max(oldValue.Count, newValue.Count); var stops = new ImmutableGradientStop[resultCount]; diff --git a/src/Avalonia.Base/Animation/CrossFade.cs b/src/Avalonia.Base/Animation/CrossFade.cs index 608a0880ec..a229bc7ce6 100644 --- a/src/Avalonia.Base/Animation/CrossFade.cs +++ b/src/Avalonia.Base/Animation/CrossFade.cs @@ -10,7 +10,7 @@ using Avalonia.VisualTree; namespace Avalonia.Animation { /// - /// Defines a cross-fade animation between two s. + /// Defines a cross-fade animation between two s. /// public class CrossFade : IPageTransition { diff --git a/src/Avalonia.Base/Animation/Cue.cs b/src/Avalonia.Base/Animation/Cue.cs index 6578148b07..c48f2ab6b0 100644 --- a/src/Avalonia.Base/Animation/Cue.cs +++ b/src/Avalonia.Base/Animation/Cue.cs @@ -8,7 +8,7 @@ namespace Avalonia.Animation /// Determines the time index for a . /// [TypeConverter(typeof(CueTypeConverter))] - public readonly struct Cue : IEquatable, IEquatable + public readonly record struct Cue : IEquatable, IEquatable { /// /// The normalized percent value, ranging from 0.0 to 1.0 @@ -49,15 +49,6 @@ namespace Avalonia.Animation } } - /// - /// Checks for equality between two s. - /// - /// The second cue. - public bool Equals(Cue other) - { - return CueValue == other.CueValue; - } - /// /// Checks for equality between a /// and a value. diff --git a/src/Avalonia.Base/Animation/KeySpline.cs b/src/Avalonia.Base/Animation/KeySpline.cs index a6e9769186..6ca5b2e759 100644 --- a/src/Avalonia.Base/Animation/KeySpline.cs +++ b/src/Avalonia.Base/Animation/KeySpline.cs @@ -196,7 +196,7 @@ namespace Avalonia.Animation /// /// /// - private bool IsValidXValue(double value) + private static bool IsValidXValue(double value) { return value >= 0.0 && value <= 1.0; } diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 21bdb794b3..0d3da66f7a 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -19,6 +19,7 @@ + @@ -30,6 +31,8 @@ + + @@ -41,6 +44,7 @@ + @@ -48,14 +52,10 @@ - + - diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index 2c89062e51..e0782c51a2 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Avalonia.Data; using Avalonia.Data.Core; using Avalonia.PropertyStore; @@ -442,6 +443,7 @@ namespace Avalonia /// /// The value. /// True if the value is valid, otherwise false. + [RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)] public bool IsValidValue(object? value) { return TypeUtilities.TryConvertImplicit(PropertyType, value, out _); diff --git a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs index 6106c58880..fc0ca2323e 100644 --- a/src/Avalonia.Base/AvaloniaPropertyRegistry.cs +++ b/src/Avalonia.Base/AvaloniaPropertyRegistry.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace Avalonia @@ -42,6 +43,7 @@ namespace Avalonia /// /// The type. /// A collection of definitions. + [UnconditionalSuppressMessage("Trimming", "IL2059", Justification = "If type was trimmed out, no properties were referenced")] public IReadOnlyList GetRegistered(Type type) { _ = type ?? throw new ArgumentNullException(nameof(type)); diff --git a/src/Avalonia.Base/AvaloniaProperty`1.cs b/src/Avalonia.Base/AvaloniaProperty`1.cs index 5a0d69f3bf..53444ee475 100644 --- a/src/Avalonia.Base/AvaloniaProperty`1.cs +++ b/src/Avalonia.Base/AvaloniaProperty`1.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Reactive.Subjects; using Avalonia.Data; using Avalonia.Utilities; @@ -67,6 +68,7 @@ namespace Avalonia protected override IObservable GetChanged() => Changed; + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)] protected BindingValue TryConvert(object? value) { if (value == UnsetValue) diff --git a/src/Avalonia.Base/Collections/AvaloniaListConverter.cs b/src/Avalonia.Base/Collections/AvaloniaListConverter.cs index b3fc0b01b6..34ccb5e65f 100644 --- a/src/Avalonia.Base/Collections/AvaloniaListConverter.cs +++ b/src/Avalonia.Base/Collections/AvaloniaListConverter.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using Avalonia.Utilities; @@ -8,7 +9,8 @@ namespace Avalonia.Collections /// /// Creates an from a string representation. /// - public class AvaloniaListConverter : TypeConverter + [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)] + public class AvaloniaListConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T> : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) { diff --git a/src/Avalonia.Base/Compatibility/TrimmingAttributes.cs b/src/Avalonia.Base/Compatibility/TrimmingAttributes.cs new file mode 100644 index 0000000000..941faa46bc --- /dev/null +++ b/src/Avalonia.Base/Compatibility/TrimmingAttributes.cs @@ -0,0 +1,121 @@ +#pragma warning disable MA0048 // File name must match type name +// https://github.com/dotnet/runtime/tree/main/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Diagnostics.CodeAnalysis +{ +#nullable enable +#if !NET6_0_OR_GREATER + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, + Inherited = false)] + internal sealed class DynamicallyAccessedMembersAttribute : Attribute + { + public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) + { + MemberTypes = memberTypes; + } + + public DynamicallyAccessedMemberTypes MemberTypes { get; } + } + + [Flags] + internal enum DynamicallyAccessedMemberTypes + { + None = 0, + PublicParameterlessConstructor = 0x0001, + PublicConstructors = 0x0002 | PublicParameterlessConstructor, + NonPublicConstructors = 0x0004, + PublicMethods = 0x0008, + NonPublicMethods = 0x0010, + PublicFields = 0x0020, + NonPublicFields = 0x0040, + PublicNestedTypes = 0x0080, + NonPublicNestedTypes = 0x0100, + PublicProperties = 0x0200, + NonPublicProperties = 0x0400, + PublicEvents = 0x0800, + NonPublicEvents = 0x1000, + Interfaces = 0x2000, + All = ~None + } + + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method, + AllowMultiple = true, Inherited = false)] + internal sealed class DynamicDependencyAttribute : Attribute + { + public DynamicDependencyAttribute(string memberSignature) + { + MemberSignature = memberSignature; + } + + public DynamicDependencyAttribute(string memberSignature, Type type) + { + MemberSignature = memberSignature; + Type = type; + } + + public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName) + { + MemberSignature = memberSignature; + TypeName = typeName; + AssemblyName = assemblyName; + } + + public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, Type type) + { + MemberTypes = memberTypes; + Type = type; + } + + public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName) + { + MemberTypes = memberTypes; + TypeName = typeName; + AssemblyName = assemblyName; + } + + public string? MemberSignature { get; } + public DynamicallyAccessedMemberTypes MemberTypes { get; } + public Type? Type { get; } + public string? TypeName { get; } + public string? AssemblyName { get; } + public string? Condition { get; set; } + } + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] + internal sealed class RequiresUnreferencedCodeAttribute : Attribute + { + public RequiresUnreferencedCodeAttribute(string message) + { + Message = message; + } + + public string Message { get; } + public string? Url { get; set; } + } + + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + internal sealed class UnconditionalSuppressMessageAttribute : Attribute + { + public UnconditionalSuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + public string Category { get; } + public string CheckId { get; } + public string? Scope { get; set; } + public string? Target { get; set; } + public string? MessageId { get; set; } + public string? Justification { get; set; } + } +#endif +} + diff --git a/src/Avalonia.Base/Controls/Classes.cs b/src/Avalonia.Base/Controls/Classes.cs index 30c6fdefa3..e41e1cc488 100644 --- a/src/Avalonia.Base/Controls/Classes.cs +++ b/src/Avalonia.Base/Controls/Classes.cs @@ -37,7 +37,7 @@ namespace Avalonia.Controls /// The initial items. public Classes(params string[] items) : base(items) - { + { } /// @@ -320,7 +320,7 @@ namespace Avalonia.Controls listener.Changed(); } - private void ThrowIfPseudoclass(string name, string operation) + private static void ThrowIfPseudoclass(string name, string operation) { if (name.StartsWith(":")) { diff --git a/src/Avalonia.Base/CornerRadius.cs b/src/Avalonia.Base/CornerRadius.cs index 893f7c4565..1666fac2e1 100644 --- a/src/Avalonia.Base/CornerRadius.cs +++ b/src/Avalonia.Base/CornerRadius.cs @@ -61,9 +61,13 @@ namespace Avalonia public double BottomLeft { get; } /// - /// Gets a value indicating whether all corner radii are set to 0. + /// Gets a value indicating whether the instance has default values (all corner radii are set to 0). /// - public bool IsEmpty => TopLeft.Equals(0) && IsUniform; + public bool IsDefault => TopLeft == 0 && TopRight == 0 && BottomLeft == 0 && BottomRight == 0; + + /// + [Obsolete("Use IsDefault instead.")] + public bool IsEmpty => IsDefault; /// /// Gets a value indicating whether all corner radii are equal. @@ -79,7 +83,6 @@ namespace Avalonia { // ReSharper disable CompareOfFloatsByEqualityOperator return TopLeft == other.TopLeft && - TopRight == other.TopRight && BottomRight == other.BottomRight && BottomLeft == other.BottomLeft; @@ -100,7 +103,7 @@ namespace Avalonia public override string ToString() { - return $"{TopLeft},{TopRight},{BottomRight},{BottomLeft}"; + return FormattableString.Invariant($"{TopLeft},{TopRight},{BottomRight},{BottomLeft}"); } public static CornerRadius Parse(string s) diff --git a/src/Avalonia.Base/Data/BindingValue.cs b/src/Avalonia.Base/Data/BindingValue.cs index 4bb3ad08d5..3bc172f596 100644 --- a/src/Avalonia.Base/Data/BindingValue.cs +++ b/src/Avalonia.Base/Data/BindingValue.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Avalonia.Utilities; namespace Avalonia.Data @@ -79,7 +80,7 @@ namespace Avalonia.Data /// - For an unset value, use or simply `default` /// - For other types, call one of the static factory methods /// - public readonly struct BindingValue + public readonly record struct BindingValue { private readonly T _value; @@ -236,6 +237,7 @@ namespace Avalonia.Data /// /// The untyped value. /// The typed binding value. + [RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)] public static BindingValue FromUntyped(object? value) { return FromUntyped(value, typeof(T)); @@ -249,6 +251,7 @@ namespace Avalonia.Data /// The untyped value. /// The runtime target type. /// The typed binding value. + [RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)] public static BindingValue FromUntyped(object? value, Type targetType) { if (value == AvaloniaProperty.UnsetValue) diff --git a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs index c4f4362537..f5c135459d 100644 --- a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows.Input; using Avalonia.Utilities; @@ -9,6 +10,7 @@ namespace Avalonia.Data.Converters /// Provides a default set of value conversions for bindings that do not specify a value /// converter. /// + [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)] public class DefaultValueConverter : IValueConverter { /// diff --git a/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs b/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs index 0672185a55..b42debc358 100644 --- a/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs +++ b/src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Linq.Expressions; @@ -9,6 +10,7 @@ using Avalonia.Utilities; namespace Avalonia.Data.Converters { + [RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)] class MethodToCommandConverter : ICommand { readonly static Func AlwaysEnabled = (_) => true; diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs index ea903c1042..f60b4722d9 100644 --- a/src/Avalonia.Base/Data/Core/BindingExpression.cs +++ b/src/Avalonia.Base/Data/Core/BindingExpression.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reactive.Linq; using System.Reactive.Subjects; @@ -13,6 +14,7 @@ namespace Avalonia.Data.Core /// Binds to an expression on an object using a type value converter to convert the values /// that are sent and received. /// + [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)] public class BindingExpression : LightweightObservableBase, ISubject, IDescription { private readonly ExpressionObserver _inner; diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs index 4f755ff140..5fb2bb5c13 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNode.cs @@ -4,8 +4,6 @@ namespace Avalonia.Data.Core { public abstract class ExpressionNode { - private static readonly object CacheInvalid = new object(); - protected static readonly WeakReference UnsetReference = new WeakReference(AvaloniaProperty.UnsetValue); @@ -159,7 +157,7 @@ namespace Avalonia.Data.Core _listening = false; } - private BindingNotification TargetNullNotification() + private static BindingNotification TargetNullNotification() { return new BindingNotification( new MarkupBindingChainException("Null value"), diff --git a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs index 0c7f576da6..2151c100e5 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reactive; using System.Reactive.Linq; @@ -48,8 +49,6 @@ namespace Avalonia.Data.Core new TaskStreamPlugin(), new ObservableStreamPlugin(), }; - - private static readonly object UninitializedValue = new object(); private readonly ExpressionNode _node; private object? _root; private Func? _rootGetter; @@ -126,6 +125,7 @@ namespace Avalonia.Data.Core /// /// A description of the expression. If null, 's string representation will be used. /// + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ExpressionSafeSupressWarningMessage)] public static ExpressionObserver Create( T? root, Expression> expression, @@ -144,6 +144,7 @@ namespace Avalonia.Data.Core /// /// A description of the expression. If null, 's string representation will be used. /// + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ExpressionSafeSupressWarningMessage)] public static ExpressionObserver Create( IObservable rootObservable, Expression> expression, @@ -168,6 +169,7 @@ namespace Avalonia.Data.Core /// /// A description of the expression. If null, 's string representation will be used. /// + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ExpressionSafeSupressWarningMessage)] public static ExpressionObserver Create( Func rootGetter, Expression> expression, @@ -283,6 +285,7 @@ namespace Avalonia.Data.Core } } + [RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)] private static ExpressionNode Parse(LambdaExpression expression, bool enableDataValidation) { return ExpressionTreeParser.Parse(expression, enableDataValidation); diff --git a/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs b/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs index d2035a592f..c48be6a175 100644 --- a/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs +++ b/src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs @@ -1,10 +1,12 @@ -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Linq.Expressions; namespace Avalonia.Data.Core.Parsers { static class ExpressionTreeParser { + [RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)] public static ExpressionNode Parse(Expression expr, bool enableDataValidation) { var visitor = new ExpressionVisitorNodeBuilder(enableDataValidation); diff --git a/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs b/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs index 1e82214d76..e1e5a705f0 100644 --- a/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs +++ b/src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace Avalonia.Data.Core.Parsers { + [RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)] class ExpressionVisitorNodeBuilder : ExpressionVisitor { private const string MultiDimensionalArrayGetterMethodName = "Get"; @@ -81,7 +83,7 @@ namespace Avalonia.Data.Core.Parsers return node; } - private T GetArgumentExpressionValue(Expression expr) + private static T GetArgumentExpressionValue(Expression expr) { try { @@ -193,7 +195,7 @@ namespace Avalonia.Data.Core.Parsers throw new ExpressionParseException(0, $"Invalid method call in binding expression: '{node.Method.DeclaringType!.AssemblyQualifiedName}.{node.Method.Name}'."); } - private PropertyInfo? TryGetPropertyFromMethod(MethodInfo method) + private static PropertyInfo? TryGetPropertyFromMethod(MethodInfo method) { var type = method.DeclaringType; return type?.GetRuntimeProperties().FirstOrDefault(prop => prop.GetMethod == method); diff --git a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs index cc6d92ceb7..34f8e568d4 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.ExceptionServices; using Avalonia.Utilities; @@ -10,6 +11,7 @@ namespace Avalonia.Data.Core.Plugins public class AvaloniaPropertyAccessorPlugin : IPropertyAccessorPlugin { /// + [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] public bool Match(object obj, string propertyName) { if (obj is AvaloniaObject o) @@ -29,6 +31,7 @@ namespace Avalonia.Data.Core.Plugins /// An interface through which future interactions with the /// property will be made. /// + [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] public IPropertyAccessor? Start(WeakReference reference, string propertyName) { _ = reference ?? throw new ArgumentNullException(nameof(reference)); diff --git a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs index 361d68dc81..bc300386b9 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -12,6 +13,7 @@ namespace Avalonia.Data.Core.Plugins public class DataAnnotationsValidationPlugin : IDataValidationPlugin { /// + [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] public bool Match(WeakReference reference, string memberName) { reference.TryGetTarget(out var target); @@ -24,11 +26,13 @@ namespace Avalonia.Data.Core.Plugins } /// + [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] public IPropertyAccessor Start(WeakReference reference, string name, IPropertyAccessor inner) { return new Accessor(reference, name, inner); } + [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] private sealed class Accessor : DataValidationBase { private readonly ValidationContext? _context; @@ -63,7 +67,7 @@ namespace Avalonia.Data.Core.Plugins } } - private Exception CreateException(IList errors) + private static Exception CreateException(IList errors) { if (errors.Count == 1) { diff --git a/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs index bf4a0a88bd..2bb8da2c74 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace Avalonia.Data.Core.Plugins @@ -9,9 +10,11 @@ namespace Avalonia.Data.Core.Plugins public class ExceptionValidationPlugin : IDataValidationPlugin { /// + [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] public bool Match(WeakReference reference, string memberName) => true; /// + [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] public IPropertyAccessor Start(WeakReference reference, string name, IPropertyAccessor inner) { return new Validator(reference, name, inner); diff --git a/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs index 88e38a8d08..5b4d7cd3a1 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Avalonia.Data.Core.Plugins { @@ -13,6 +14,7 @@ namespace Avalonia.Data.Core.Plugins /// A weak reference to the object. /// The name of the member to validate. /// True if the plugin can handle the object; otherwise false. + [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] bool Match(WeakReference reference, string memberName); /// @@ -25,6 +27,7 @@ namespace Avalonia.Data.Core.Plugins /// An interface through which future interactions with the /// property will be made. /// + [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] IPropertyAccessor Start(WeakReference reference, string propertyName, IPropertyAccessor inner); diff --git a/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs index f000427de3..04601bc8b2 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Avalonia.Data.Core.Plugins { @@ -14,6 +15,7 @@ namespace Avalonia.Data.Core.Plugins /// The object. /// The property name. /// True if the plugin can handle the property on the object; otherwise false. + [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] bool Match(object obj, string propertyName); /// @@ -25,6 +27,7 @@ namespace Avalonia.Data.Core.Plugins /// An interface through which future interactions with the /// property will be made. /// + [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] IPropertyAccessor? Start(WeakReference reference, string propertyName); } diff --git a/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs index b741cfaca2..8bf2d6c3d6 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Avalonia.Data.Core.Plugins { @@ -12,6 +13,7 @@ namespace Avalonia.Data.Core.Plugins /// /// A weak reference to the value. /// True if the plugin can handle the value; otherwise false. + [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)] bool Match(WeakReference reference); /// @@ -21,6 +23,7 @@ namespace Avalonia.Data.Core.Plugins /// /// An observable that produces the output for the value. /// + [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)] IObservable Start(WeakReference reference); } } diff --git a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs index 1e7a0d5c8f..87a2f67ee8 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Avalonia.Utilities; @@ -18,6 +19,7 @@ namespace Avalonia.Data.Core.Plugins ); /// + [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] public bool Match(WeakReference reference, string memberName) { reference.TryGetTarget(out var target); @@ -26,6 +28,7 @@ namespace Avalonia.Data.Core.Plugins } /// + [RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)] public IPropertyAccessor Start(WeakReference reference, string name, IPropertyAccessor accessor) { return new Validator(reference, name, accessor); @@ -108,7 +111,7 @@ namespace Avalonia.Data.Core.Plugins return target; } - private Exception GenerateException(IList errors) + private static Exception GenerateException(IList errors) { if (errors.Count == 1) { diff --git a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs index 91d69b5d3d..5b19e995cc 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using Avalonia.Utilities; @@ -17,6 +17,7 @@ namespace Avalonia.Data.Core.Plugins new Dictionary<(Type, string), PropertyInfo?>(); /// + [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj, propertyName) != null; /// @@ -28,6 +29,7 @@ namespace Avalonia.Data.Core.Plugins /// An interface through which future interactions with the /// property will be made. /// + [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] public IPropertyAccessor? Start(WeakReference reference, string propertyName) { _ = reference ?? throw new ArgumentNullException(nameof(reference)); @@ -52,7 +54,8 @@ namespace Avalonia.Data.Core.Plugins private const BindingFlags PropertyBindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; - + + [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] private PropertyInfo? GetFirstPropertyWithName(object instance, string propertyName) { if (instance is IReflectableType reflectableType && instance is not Type) @@ -70,7 +73,8 @@ namespace Avalonia.Data.Core.Plugins return propertyInfo; } - private PropertyInfo? TryFindAndCacheProperty(Type type, string propertyName) + private PropertyInfo? TryFindAndCacheProperty( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, string propertyName) { PropertyInfo? found = null; diff --git a/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs index 0d51a6ed36..2397ce483d 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; @@ -10,8 +11,10 @@ namespace Avalonia.Data.Core.Plugins private readonly Dictionary<(Type, string), MethodInfo?> _methodLookup = new Dictionary<(Type, string), MethodInfo?>(); + [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] public bool Match(object obj, string methodName) => GetFirstMethodWithName(obj.GetType(), methodName) != null; + [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] public IPropertyAccessor? Start(WeakReference reference, string methodName) { _ = reference ?? throw new ArgumentNullException(nameof(reference)); @@ -34,7 +37,8 @@ namespace Avalonia.Data.Core.Plugins } } - private MethodInfo? GetFirstMethodWithName(Type type, string methodName) + private MethodInfo? GetFirstMethodWithName( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type type, string methodName) { var key = (type, methodName); @@ -46,7 +50,8 @@ namespace Avalonia.Data.Core.Plugins return methodInfo; } - private MethodInfo? TryFindAndCacheMethod(Type type, string methodName) + private MethodInfo? TryFindAndCacheMethod( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type type, string methodName) { MethodInfo? found = null; diff --git a/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs index 6232fa39a1..ebee4586db 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reactive.Linq; using System.Reflection; @@ -8,6 +9,7 @@ namespace Avalonia.Data.Core.Plugins /// /// Handles binding to s for the '^' stream binding operator. /// + [UnconditionalSuppressMessage("Trimming", "IL3050", Justification = TrimmingMessages.IgnoreNativeAotSupressWarningMessage)] public class ObservableStreamPlugin : IStreamPlugin { static MethodInfo? observableSelect; @@ -17,6 +19,7 @@ namespace Avalonia.Data.Core.Plugins /// /// A weak reference to the value. /// True if the plugin can handle the value; otherwise false. + [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)] public virtual bool Match(WeakReference reference) { reference.TryGetTarget(out var target); @@ -33,6 +36,7 @@ namespace Avalonia.Data.Core.Plugins /// /// An observable that produces the output for the value. /// + [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)] public virtual IObservable Start(WeakReference reference) { if (!reference.TryGetTarget(out var target) || target is null) @@ -65,6 +69,7 @@ namespace Avalonia.Data.Core.Plugins new object[] { target, box })!; } + [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)] private static MethodInfo GetObservableSelect(Type source) { return GetObservableSelect().MakeGenericMethod(source, typeof(object)); diff --git a/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs index 377ea9f275..5203aa9f57 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Reflection; @@ -9,6 +10,7 @@ namespace Avalonia.Data.Core.Plugins /// /// Handles binding to s for the '^' stream binding operator. /// + [UnconditionalSuppressMessage("Trimming", "IL3050", Justification = TrimmingMessages.IgnoreNativeAotSupressWarningMessage)] public class TaskStreamPlugin : IStreamPlugin { /// @@ -16,12 +18,13 @@ namespace Avalonia.Data.Core.Plugins /// /// A weak reference to the value. /// True if the plugin can handle the value; otherwise false. + [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)] public virtual bool Match(WeakReference reference) { reference.TryGetTarget(out var target); return target is Task; - } + } /// /// Starts producing output based on the specified value. @@ -30,6 +33,7 @@ namespace Avalonia.Data.Core.Plugins /// /// An observable that produces the output for the value. /// + [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)] public virtual IObservable Start(WeakReference reference) { reference.TryGetTarget(out var target); @@ -59,7 +63,8 @@ namespace Avalonia.Data.Core.Plugins return Observable.Empty(); } - private IObservable HandleCompleted(Task task) + [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)] + private static IObservable HandleCompleted(Task task) { var resultProperty = task.GetType().GetRuntimeProperty("Result"); diff --git a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs index c13c6fcbc8..1b79fed6e7 100644 --- a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs +++ b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs @@ -1,9 +1,10 @@ using System; -using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Avalonia.Data.Core.Plugins; namespace Avalonia.Data.Core { + [RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)] public class PropertyAccessorNode : SettableNode { private readonly bool _enableValidation; diff --git a/src/Avalonia.Base/Data/Core/StreamNode.cs b/src/Avalonia.Base/Data/Core/StreamNode.cs index 133e6a75f1..6dc6d07184 100644 --- a/src/Avalonia.Base/Data/Core/StreamNode.cs +++ b/src/Avalonia.Base/Data/Core/StreamNode.cs @@ -1,9 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Reactive.Linq; using Avalonia.Data.Core.Plugins; namespace Avalonia.Data.Core { + [RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)] public class StreamNode : ExpressionNode { private IStreamPlugin? _customPlugin = null; diff --git a/src/Avalonia.Base/Diagnostics/TrimmingMessages.cs b/src/Avalonia.Base/Diagnostics/TrimmingMessages.cs new file mode 100644 index 0000000000..a893256d17 --- /dev/null +++ b/src/Avalonia.Base/Diagnostics/TrimmingMessages.cs @@ -0,0 +1,30 @@ +namespace Avalonia; + +internal static class TrimmingMessages +{ + public const string ImplicitTypeConvertionSupressWarningMessage = "Implicit convertion methods might be removed by the linker. We don't have a reliable way to prevent it, except converting everything in compile time when possible."; + public const string ImplicitTypeConvertionRequiresUnreferencedCodeMessage = "Implicit convertion methods are required for type conversion."; + + public const string TypeConvertionSupressWarningMessage = "Convertion methods might be removed by the linker. We don't have a reliable way to prevent it, except converting everything in compile time when possible."; + public const string TypeConvertionRequiresUnreferencedCodeMessage = "Convertion methods are required for type conversion, including op_Implicit, op_Explicit, Parse and TypeConverter."; + + public const string ReflectionBindingRequiresUnreferencedCodeMessage = "BindingExpression and ReflectionBinding heavily use reflection. Consider using CompiledBindings instead."; + public const string ReflectionBindingSupressWarningMessage = "BindingExpression and ReflectionBinding internal heavily use reflection."; + + public const string CompiledBindingSafeSupressWarningMessage = "CompiledBinding preserves members used in the expression tree."; + + public const string ExpressionNodeRequiresUnreferencedCodeMessage = "ExpressionNode might require unreferenced code."; + public const string ExpressionSafeSupressWarningMessage = "Typed Expressions preserves members used in the expression tree."; + + public const string SelectorsParseRequiresUnreferencedCodeMessage = "Selectors runtime parser might require unreferenced code. Consider using stronly typed selectors factory with 'new Style(s => s.OfType