diff --git a/.editorconfig b/.editorconfig index 25e0135725..cb589a5ce1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,6 +21,7 @@ csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_between_query_expression_clauses = true +trim_trailing_whitespace = true # Indentation preferences csharp_indent_block_contents = true diff --git a/Avalonia.sln b/Avalonia.sln index c8e513f94c..25c7daf080 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -72,8 +72,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog", "samples\C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.TestApp", "tests\Avalonia.DesignerSupport.TestApp\Avalonia.DesignerSupport.TestApp.csproj", "{F1381F98-4D24-409A-A6C5-1C5B1E08BB08}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualizationDemo", "samples\VirtualizationDemo\VirtualizationDemo.csproj", "{FBCAF3D0-2808-4934-8E96-3F607594517B}" @@ -97,6 +95,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\DevAnalyzers.props = build\DevAnalyzers.props build\EmbedXaml.props = build\EmbedXaml.props build\HarfBuzzSharp.props = build\HarfBuzzSharp.props + build\ImageSharp.props = build\ImageSharp.props build\JetBrains.Annotations.props = build\JetBrains.Annotations.props build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props build\Microsoft.CSharp.props = build\Microsoft.CSharp.props @@ -117,7 +116,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\System.Memory.props = build\System.Memory.props build\UnitTests.NetFX.props = build\UnitTests.NetFX.props build\XUnit.props = build\XUnit.props - build\ImageSharp.props = build\ImageSharp.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}" @@ -169,8 +167,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformSanityChecks", "sam EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.UnitTests", "tests\Avalonia.ReactiveUI.UnitTests\Avalonia.ReactiveUI.UnitTests.csproj", "{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ColorPicker", "src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj", "{1ECC012A-8837-4AE2-9BDA-3E2857898727}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid", "src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj", "{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Dialogs", "src\Avalonia.Dialogs\Avalonia.Dialogs.csproj", "{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}" @@ -179,8 +175,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.FreeDesktop", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.UnitTests", "tests\Avalonia.Controls.DataGrid.UnitTests\Avalonia.Controls.DataGrid.UnitTests.csproj", "{351337F5-D66F-461B-A957-4EF60BDB4BA6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample", "samples\interop\NativeEmbedSample\NativeEmbedSample.csproj", "{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless", "src\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}" @@ -209,1786 +203,304 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsInteropTest", "sampl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\SampleControls\ControlSamples.csproj", "{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", "src\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj", "{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", "src\Avalonia.SourceGenerator\Avalonia.SourceGenerator.csproj", "{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ColorPicker", "src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj", "{7BF6C69D-FC14-43EB-9ED0-782C16F3D5D9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{EABE2161-989B-42BF-BD8D-1E34B20C21F1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Ad-Hoc|Any CPU = Ad-Hoc|Any CPU - Ad-Hoc|iPhone = Ad-Hoc|iPhone - Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator - AppStore|Any CPU = AppStore|Any CPU - AppStore|iPhone = AppStore|iPhone - AppStore|iPhoneSimulator = AppStore|iPhoneSimulator Debug|Any CPU = Debug|Any CPU - Debug|iPhone = Debug|iPhone - Debug|iPhoneSimulator = Debug|iPhoneSimulator Release|Any CPU = Release|Any CPU - Release|iPhone = Release|iPhone - Release|iPhoneSimulator = Release|iPhoneSimulator EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|Any CPU.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|iPhone.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|iPhone.Build.0 = Debug|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|Any CPU.ActiveCfg = Release|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|Any CPU.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhone.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhone.Build.0 = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|Any CPU.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|iPhone.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|Any CPU.Build.0 = Debug|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|iPhone.Build.0 = Debug|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|Any CPU.ActiveCfg = Release|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|Any CPU.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|iPhone.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|iPhone.Build.0 = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|Any CPU.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|iPhone.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|iPhone.Build.0 = Debug|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|Any CPU.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhone.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhone.Build.0 = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|Any CPU.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|iPhone.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|iPhone.Build.0 = Debug|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|Any CPU.ActiveCfg = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|Any CPU.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhone.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhone.Build.0 = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|Any CPU.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|iPhone.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|iPhone.Build.0 = Debug|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|Any CPU.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhone.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhone.Build.0 = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|Any CPU.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|iPhone.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|iPhone.Build.0 = Debug|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|Any CPU.ActiveCfg = Release|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|Any CPU.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhone.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhone.Build.0 = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|Any CPU.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|iPhone.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|iPhone.Build.0 = Debug|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|Any CPU.ActiveCfg = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|Any CPU.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhone.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhone.Build.0 = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|Any CPU.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|iPhone.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|iPhone.Build.0 = Debug|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|Any CPU.ActiveCfg = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|Any CPU.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhone.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhone.Build.0 = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|Any CPU.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|iPhone.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|iPhone.Build.0 = Debug|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.ActiveCfg = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhone.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhone.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|Any CPU.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|iPhone.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|iPhone.Build.0 = Debug|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|Any CPU.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|iPhone.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|iPhone.Build.0 = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|Any CPU.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|iPhone.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|Any CPU.Build.0 = Debug|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|iPhone.Build.0 = Debug|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|Any CPU.ActiveCfg = Release|Any CPU {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|Any CPU.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|iPhone.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|iPhone.Build.0 = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {99135EAB-653D-47E4-A378-C96E1278CA44}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|Any CPU.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|iPhone.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|iPhone.Build.0 = Debug|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|Any CPU.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|iPhone.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|iPhone.Build.0 = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3E53A01A-B331-47F3-B828-4A5717E77A24}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|Any CPU.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|iPhone.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|iPhone.Build.0 = Debug|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|Any CPU.ActiveCfg = Release|Any CPU {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|Any CPU.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|iPhone.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|iPhone.Build.0 = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|Any CPU.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|iPhone.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|iPhone.Build.0 = Debug|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {6417E941-21BC-467B-A771-0DE389353CE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {6417E941-21BC-467B-A771-0DE389353CE6}.Release|Any CPU.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Release|iPhone.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Release|iPhone.Build.0 = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6417E941-21BC-467B-A771-0DE389353CE6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|Any CPU.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|iPhone.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|iPhone.Build.0 = Debug|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|Any CPU.ActiveCfg = Release|Any CPU {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|Any CPU.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|iPhone.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|iPhone.Build.0 = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8EF392D5-1416-45AA-9956-7CBBC3229E8A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|Any CPU.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|iPhone.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|iPhone.Build.0 = Debug|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|Any CPU.ActiveCfg = Release|Any CPU {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|Any CPU.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|iPhone.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|iPhone.Build.0 = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|Any CPU.ActiveCfg = Release|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|Any CPU.Build.0 = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|iPhone.ActiveCfg = Release|Any CPU - {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|Any CPU.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|iPhone.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|iPhone.Build.0 = Debug|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|Any CPU.ActiveCfg = Release|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|Any CPU.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|iPhone.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|iPhone.Build.0 = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|Any CPU.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|iPhone.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|iPhone.Build.0 = Debug|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|Any CPU.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|iPhone.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|iPhone.Build.0 = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E1AA3DBF-9056-4530-9376-18119A7A3FFE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|Any CPU.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhone.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhone.Build.0 = Debug|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|Any CPU.ActiveCfg = Release|Any CPU {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|Any CPU.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhone.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhone.Build.0 = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {88060192-33D5-4932-B0F9-8BD2763E857D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|Any CPU.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhone.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhone.Build.0 = Debug|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|Any CPU.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhone.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhone.Build.0 = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|Any CPU.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhone.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhone.Build.0 = Debug|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|Any CPU.ActiveCfg = Release|Any CPU {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|Any CPU.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhone.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhone.Build.0 = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|Any CPU.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|iPhone.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|iPhone.Build.0 = Debug|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|Any CPU.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|iPhone.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|iPhone.Build.0 = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|Any CPU.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|iPhone.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|iPhone.Build.0 = Debug|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|Any CPU.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhone.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhone.Build.0 = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2B888490-D14A-4BCA-AB4B-48676FA93C9B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|Any CPU.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|iPhone.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|iPhone.Build.0 = Debug|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Release|Any CPU.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Release|iPhone.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Release|iPhone.Build.0 = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {52F55355-D120-42AC-8116-8410A7D602FA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|Any CPU.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|iPhone.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|iPhone.Build.0 = Debug|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|Any CPU.ActiveCfg = Release|Any CPU {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|Any CPU.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|iPhone.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|iPhone.Build.0 = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1381F98-4D24-409A-A6C5-1C5B1E08BB08}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|Any CPU.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|iPhone.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|iPhone.Build.0 = Debug|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|Any CPU.ActiveCfg = Release|Any CPU {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|Any CPU.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|iPhone.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|iPhone.Build.0 = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FBCAF3D0-2808-4934-8E96-3F607594517B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|Any CPU.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|iPhone.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|iPhone.Build.0 = Debug|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|Any CPU.ActiveCfg = Release|Any CPU {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|Any CPU.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|iPhone.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|iPhone.Build.0 = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1FDC5B0-4654-416F-AE69-E3E9BBD87801}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Any CPU.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|Any CPU.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhone.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhone.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|Any CPU.Build.0 = Debug|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhone.Build.0 = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhone.Deploy.0 = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|Any CPU.ActiveCfg = Release|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|Any CPU.Build.0 = Release|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|Any CPU.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhone.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhone.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhone.Deploy.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|Any CPU.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|iPhone.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|iPhone.Build.0 = Debug|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|Any CPU.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhone.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhone.Build.0 = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7D2D3083-71DD-4CC9-8907-39A0D86FB322}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhone.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhone.Build.0 = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Any CPU.Build.0 = Release|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhone.ActiveCfg = Release|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhone.Build.0 = Release|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhone.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhone.Build.0 = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhoneSimulator.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 - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhone.ActiveCfg = Release|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhone.Build.0 = Release|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhone.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhoneSimulator.Build.0 = Debug|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}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhone.Build.0 = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhoneSimulator.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 - {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhone.ActiveCfg = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhone.Build.0 = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Any CPU.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhone.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhoneSimulator.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}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhone.Build.0 = Debug|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.ActiveCfg = Release|Any CPU {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhone.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhone.Build.0 = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|iPhone.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Debug|iPhone.Build.0 = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Release|Any CPU.Build.0 = Release|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Release|iPhone.ActiveCfg = Release|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Release|iPhone.Build.0 = Release|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E1582370-37B3-403C-917F-8209551B1634}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhone.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhone.Build.0 = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Any CPU.ActiveCfg = Release|Any CPU {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Any CPU.Build.0 = Release|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhone.ActiveCfg = Release|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhone.Build.0 = Release|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhone.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhone.Build.0 = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Any CPU.ActiveCfg = Release|Any CPU {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Any CPU.Build.0 = Release|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhone.ActiveCfg = Release|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhone.Build.0 = Release|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|Any CPU.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhone.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhone.Build.0 = Debug|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {050CC912-FF49-4A8B-B534-9544017446DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {050CC912-FF49-4A8B-B534-9544017446DD}.Release|Any CPU.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhone.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhone.Build.0 = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {050CC912-FF49-4A8B-B534-9544017446DD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|Any CPU.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhone.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhone.Build.0 = Debug|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Any CPU.ActiveCfg = Release|Any CPU {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|Any CPU.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhone.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhone.Build.0 = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F40FC0A2-1BC3-401C-BFC1-928EC4D4A9CE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|iPhone.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|iPhone.Build.0 = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|Any CPU.Build.0 = Release|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhone.ActiveCfg = Release|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhone.Build.0 = Release|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E1240B49-7B4B-4371-A00E-068778C5CF0B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhone.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhone.Build.0 = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|Any CPU.ActiveCfg = Release|Any CPU {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|Any CPU.Build.0 = Release|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhone.ActiveCfg = Release|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhone.Build.0 = Release|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhone.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|Any CPU.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhone.Build.0 = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|Any CPU.ActiveCfg = Release|Any CPU {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|Any CPU.Build.0 = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.ActiveCfg = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.Build.0 = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|iPhone.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|iPhone.Build.0 = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|Any CPU.ActiveCfg = Release|Any CPU {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|Any CPU.Build.0 = Release|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|iPhone.ActiveCfg = Release|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|iPhone.Build.0 = Release|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D49233F8-F29C-47DD-9975-C4C9E4502720}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|iPhone.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|iPhone.Build.0 = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|Any CPU.ActiveCfg = Release|Any CPU {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|Any CPU.Build.0 = Release|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhone.ActiveCfg = Release|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhone.Build.0 = Release|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3C471044-3640-45E3-B1B2-16D2FF8399EE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhone.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhone.Build.0 = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|Any CPU.Build.0 = Release|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.ActiveCfg = Release|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhone.Build.0 = Release|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {BF28998D-072C-439A-AFBB-2FE5021241E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|Any CPU.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhone.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhone.Build.0 = Debug|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|Any CPU.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.Build.0 = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhone.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Debug|Any CPU.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhone.Build.0 = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Release|Any CPU.ActiveCfg = Release|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Release|Any CPU.Build.0 = Release|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhone.ActiveCfg = Release|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhone.Build.0 = Release|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {41B02319-965D-4945-8005-C1A3D1224165}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhone.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhone.Build.0 = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|Any CPU.Build.0 = Release|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhone.ActiveCfg = Release|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhone.Build.0 = Release|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|iPhone.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|iPhone.Build.0 = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|Any CPU.Build.0 = Release|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhone.ActiveCfg = Release|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhone.Build.0 = Release|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|iPhone.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|iPhone.Build.0 = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|Any CPU.ActiveCfg = Release|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|Any CPU.Build.0 = Release|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhone.ActiveCfg = Release|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhone.Build.0 = Release|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhone.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhone.Build.0 = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|Any CPU.ActiveCfg = Release|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|Any CPU.Build.0 = Release|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhone.ActiveCfg = Release|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhone.Build.0 = Release|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhone.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhone.Build.0 = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|Any CPU.ActiveCfg = Release|Any CPU {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|Any CPU.Build.0 = Release|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhone.ActiveCfg = Release|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhone.Build.0 = Release|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|iPhone.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|iPhone.Build.0 = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|Any CPU.ActiveCfg = Release|Any CPU {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|Any CPU.Build.0 = Release|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhone.ActiveCfg = Release|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhone.Build.0 = Release|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhone.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhone.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|Any CPU.Build.0 = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhone.ActiveCfg = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhone.Build.0 = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhone.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhone.Build.0 = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|Any CPU.Build.0 = Release|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhone.ActiveCfg = Release|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhone.Build.0 = Release|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhone.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhone.Build.0 = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|Any CPU.Build.0 = Release|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhone.ActiveCfg = Release|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhone.Build.0 = Release|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhone.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhone.Build.0 = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|Any CPU.ActiveCfg = Release|Any CPU {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|Any CPU.Build.0 = Release|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhone.ActiveCfg = Release|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhone.Build.0 = Release|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhone.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|Any CPU.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhone.Build.0 = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|Any CPU.ActiveCfg = Release|Any CPU {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|Any CPU.Build.0 = Release|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.ActiveCfg = Release|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.Build.0 = Release|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|Any CPU.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|iPhone.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|Any CPU.Build.0 = Debug|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|iPhone.Build.0 = Debug|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|Any CPU.ActiveCfg = Release|Any CPU {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|Any CPU.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhone.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhone.Build.0 = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|iPhone.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|iPhone.Build.0 = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|Any CPU.ActiveCfg = Release|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|Any CPU.Build.0 = Release|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhone.ActiveCfg = Release|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhone.Build.0 = Release|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhone.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhone.Build.0 = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|Any CPU.Build.0 = Release|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.ActiveCfg = Release|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.Build.0 = Release|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhone.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|Any CPU.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhone.Build.0 = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|Any CPU.ActiveCfg = Release|Any CPU {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|Any CPU.Build.0 = Release|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhone.ActiveCfg = Release|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhone.Build.0 = Release|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {676D6BFD-029D-4E43-BFC7-3892265CE251}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhone.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhone.Build.0 = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.ActiveCfg = Release|Any CPU {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.Build.0 = Release|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhone.ActiveCfg = Release|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhone.Build.0 = Release|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|iPhone.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|iPhone.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.Build.0 = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Release|iPhone.ActiveCfg = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Release|iPhone.Build.0 = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|iPhone.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|iPhone.Build.0 = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|Any CPU.ActiveCfg = Release|Any CPU {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|Any CPU.Build.0 = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|iPhone.ActiveCfg = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|iPhone.Build.0 = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {C08E9894-AA92-426E-BF56-033E262CAD3E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|iPhone.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|iPhone.Build.0 = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|Any CPU.Build.0 = Release|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|iPhone.ActiveCfg = Release|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|iPhone.Build.0 = Release|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|iPhone.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|iPhone.Build.0 = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|Any CPU.ActiveCfg = Release|Any CPU {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|Any CPU.Build.0 = Release|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhone.ActiveCfg = Release|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhone.Build.0 = Release|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhone.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhone.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|Any CPU.Build.0 = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhone.ActiveCfg = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhone.Build.0 = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {E8A597F0-2AB5-4BDA-A235-41162DAF53CF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhone.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhone.Build.0 = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|Any CPU.Build.0 = Release|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhone.ActiveCfg = Release|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhone.Build.0 = Release|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhone.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhone.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|Any CPU.Build.0 = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhone.ActiveCfg = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhone.Build.0 = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.Build.0 = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.ActiveCfg = Release|Any CPU {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.Build.0 = Release|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.ActiveCfg = Release|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.Build.0 = Release|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhone.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhone.Build.0 = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|Any CPU.Build.0 = Release|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.ActiveCfg = Release|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.Build.0 = Release|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.Build.0 = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.ActiveCfg = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {7BF6C69D-FC14-43EB-9ED0-782C16F3D5D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BF6C69D-FC14-43EB-9ED0-782C16F3D5D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BF6C69D-FC14-43EB-9ED0-782C16F3D5D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BF6C69D-FC14-43EB-9ED0-782C16F3D5D9}.Release|Any CPU.Build.0 = Release|Any CPU + {EABE2161-989B-42BF-BD8D-1E34B20C21F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EABE2161-989B-42BF-BD8D-1E34B20C21F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EABE2161-989B-42BF-BD8D-1E34B20C21F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EABE2161-989B-42BF-BD8D-1E34B20C21F1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2012,7 +524,6 @@ Global {410AC439-81A1-4EB5-B5E9-6A7FC6B77F4B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {D0A739B9-3C68-4BA6-A328-41606954B6BD} = {9B9E3891-2366-4253-A952-D08BCEB71098} {2B888490-D14A-4BCA-AB4B-48676FA93C9B} = {9B9E3891-2366-4253-A952-D08BCEB71098} - {52F55355-D120-42AC-8116-8410A7D602FA} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {F1381F98-4D24-409A-A6C5-1C5B1E08BB08} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {FBCAF3D0-2808-4934-8E96-3F607594517B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {A0CC0258-D18C-4AB3-854F-7101680FC3F9} = {9B9E3891-2366-4253-A952-D08BCEB71098} @@ -2020,7 +531,6 @@ Global {29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098} {7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E} {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098} - {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} = {A689DEF5-D50F-4975-8B72-124C9EB54066} {854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888} @@ -2035,7 +545,6 @@ Global {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {3C84E04B-36CF-4D0D-B965-C26DD649D1F3} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C} {11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098} {BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098} @@ -2046,8 +555,8 @@ Global {26A98DA1-D89D-4A95-8152-349F404DA2E2} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270} = {9B9E3891-2366-4253-A952-D08BCEB71098} {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098} - {CE910927-CE5A-456F-BC92-E4C757354A5C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} + {EABE2161-989B-42BF-BD8D-1E34B20C21F1} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/Directory.Build.props b/Directory.Build.props index 97781b7517..42daa2df7f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,4 +1,5 @@ + $(MSBuildThisFileDirectory)build-intermediate/nuget $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll diff --git a/build/ApiCompatAttributeExcludeList.txt b/build/ApiCompatAttributeExcludeList.txt new file mode 100644 index 0000000000..1df5a30ec3 --- /dev/null +++ b/build/ApiCompatAttributeExcludeList.txt @@ -0,0 +1,2 @@ +T:Avalonia.Metadata.NotClientImplementableAttribute +T:Avalonia.Metadata.UnstableAttribute diff --git a/build/AvaloniaPublicKey.props b/build/AvaloniaPublicKey.props new file mode 100644 index 0000000000..89215635c0 --- /dev/null +++ b/build/AvaloniaPublicKey.props @@ -0,0 +1,5 @@ + + + 0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87 + + diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props index 314d38190a..00a1e3094b 100644 --- a/build/CoreLibraries.props +++ b/build/CoreLibraries.props @@ -3,13 +3,10 @@ - - - diff --git a/build/HarfBuzzSharp.props b/build/HarfBuzzSharp.props index e10de93530..85e7a1f34d 100644 --- a/build/HarfBuzzSharp.props +++ b/build/HarfBuzzSharp.props @@ -1,7 +1,7 @@  - - - + + + diff --git a/build/JetBrains.dotMemoryUnit.props b/build/JetBrains.dotMemoryUnit.props index eb4e2b6f15..5d74d474cf 100644 --- a/build/JetBrains.dotMemoryUnit.props +++ b/build/JetBrains.dotMemoryUnit.props @@ -1,5 +1,5 @@ - + diff --git a/build/NetFX.props b/build/NetFX.props index 8ffc9ec561..14adb54035 100644 --- a/build/NetFX.props +++ b/build/NetFX.props @@ -1,7 +1,6 @@  - diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index a217a8272d..d54cffba08 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,7 +1,7 @@  - - - + + + diff --git a/build/SourceLink.props b/build/SourceLink.props index 9f05848881..dd7ecc8d2a 100644 --- a/build/SourceLink.props +++ b/build/SourceLink.props @@ -19,7 +19,7 @@ - + diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm index 314c579b76..0fa4540726 100644 --- a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm @@ -85,7 +85,7 @@ _settingSize = true; [super setFrameSize:newSize]; - auto window = static_cast>([self window]); + auto window = (id ) [self window]; // TODO get actual titlebar size diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm index 2365189010..b49005de8a 100644 --- a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm @@ -3,8 +3,6 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // -#pragma once - #define IS_NSPANEL #include "AvnWindow.mm" diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index 02526afbcb..01725ace03 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -222,7 +222,7 @@ - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type { - bool triggerInputWhenDisabled = type != Move; + bool triggerInputWhenDisabled = type != Move && type != LeaveWindow; if([self ignoreUserInput: triggerInputWhenDisabled]) { @@ -439,7 +439,12 @@ if(_parent != nullptr) { - _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key); + auto handled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key); + if (key != LeftCtrl && key != RightCtrl) { + _lastKeyHandled = handled; + } else { + _lastKeyHandled = false; + } } } @@ -709,4 +714,4 @@ return [[self accessibilityChild] accessibilityFocusedUIElement]; } -@end \ No newline at end of file +@end diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 6ff19ead68..ebd9f39d30 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -31,7 +31,9 @@ ComPtr _parent; bool _closed; bool _isEnabled; + bool _canBecomeKeyWindow; bool _isExtended; + bool _isTransitioningToFullScreen; AvnMenu* _menu; } @@ -67,7 +69,7 @@ } } -- (void)performClose:(id)sender +- (void)performClose:(id _Nullable )sender { if([[self delegate] respondsToSelector:@selector(windowShouldClose:)]) { @@ -146,7 +148,7 @@ } } --(void) applyMenu:(AvnMenu *)menu +-(void) applyMenu:(AvnMenu *_Nullable)menu { if(menu == nullptr) { @@ -156,7 +158,7 @@ _menu = menu; } --(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +-(CLASS_NAME*_Nonnull) initWithParent: (WindowBaseImpl*_Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ // create nswindow with specific contentRect, otherwise we wont be able to resize the window @@ -174,10 +176,17 @@ [self setBackgroundColor: [NSColor clearColor]]; _isExtended = false; + _isTransitioningToFullScreen = false; + + if(self.isDialog) + { + [self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorFullScreenAuxiliary]; + } + return self; } -- (BOOL)windowShouldClose:(NSWindow *)sender +- (BOOL)windowShouldClose:(NSWindow *_Nonnull)sender { auto window = dynamic_cast(_parent.getRaw()); @@ -189,19 +198,28 @@ return true; } -- (void)windowDidChangeBackingProperties:(NSNotification *)notification +- (void)windowDidChangeBackingProperties:(NSNotification *_Nonnull)notification { [self backingScaleFactor]; } -- (void)windowWillClose:(NSNotification *)notification + + +- (void)windowWillClose:(NSNotification *_Nonnull)notification { _closed = true; if(_parent) { ComPtr parent = _parent; _parent = NULL; - [self restoreParentWindow]; + + auto window = dynamic_cast(parent.getRaw()); + + if(window != nullptr) + { + window->SetParent(nullptr); + } + parent->BaseEvents->Closed(); [parent->View onClosed]; } @@ -209,26 +227,32 @@ -(BOOL)canBecomeKeyWindow { - // If the window has a child window being shown as a dialog then don't allow it to become the key window. - for(NSWindow* uch in [self childWindows]) + if(_canBecomeKeyWindow) { - auto ch = static_cast>(uch); - if(ch == nil) - continue; - if (ch.isDialog) - return false; - } + // If the window has a child window being shown as a dialog then don't allow it to become the key window. + auto parent = dynamic_cast(_parent.getRaw()); + + if(parent != nullptr) + { + return parent->CanBecomeKeyWindow(); + } - return true; + return true; + } + + return false; } +#ifndef IS_NSPANEL -(BOOL)canBecomeMainWindow { -#ifdef IS_NSPANEL - return false; -#else return true; +} #endif + +-(void)setCanBecomeKeyWindow:(bool)value +{ + _canBecomeKeyWindow = value; } -(bool)shouldTryToHandleEvents @@ -239,6 +263,10 @@ -(void) setEnabled:(bool)enable { _isEnabled = enable; + + [[self standardWindowButton:NSWindowCloseButton] setEnabled:enable]; + [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:enable]; + [[self standardWindowButton:NSWindowZoomButton] setEnabled:enable]; } -(void)becomeKeyWindow @@ -253,17 +281,20 @@ [super becomeKeyWindow]; } --(void) restoreParentWindow; +- (void)windowDidBecomeKey:(NSNotification *_Nonnull)notification { - auto parent = [self parentWindow]; - - if(parent != nil) - { - [parent removeChildWindow:self]; - } + _parent->BringToFront(); + + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + [self invalidateShadow]; + } + @finally{ + } + }); } -- (void)windowDidMiniaturize:(NSNotification *)notification +- (void)windowDidMiniaturize:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -273,7 +304,7 @@ } } -- (void)windowDidDeminiaturize:(NSNotification *)notification +- (void)windowDidDeminiaturize:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -283,7 +314,7 @@ } } -- (void)windowDidResize:(NSNotification *)notification +- (void)windowDidResize:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -293,7 +324,7 @@ } } -- (void)windowWillExitFullScreen:(NSNotification *)notification +- (void)windowWillExitFullScreen:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -303,7 +334,7 @@ } } -- (void)windowDidExitFullScreen:(NSNotification *)notification +- (void)windowDidExitFullScreen:(NSNotification *_Nonnull)notification { auto parent = dynamic_cast(_parent.operator->()); @@ -326,8 +357,9 @@ } } -- (void)windowWillEnterFullScreen:(NSNotification *)notification +- (void)windowWillEnterFullScreen:(NSNotification *_Nonnull)notification { + _isTransitioningToFullScreen = true; auto parent = dynamic_cast(_parent.operator->()); if(parent != nullptr) @@ -336,8 +368,9 @@ } } -- (void)windowDidEnterFullScreen:(NSNotification *)notification +- (void)windowDidEnterFullScreen:(NSNotification *_Nonnull)notification { + _isTransitioningToFullScreen = false; auto parent = dynamic_cast(_parent.operator->()); if(parent != nullptr) @@ -347,7 +380,7 @@ } } -- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame +- (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame { return true; } @@ -358,11 +391,13 @@ _parent->BaseEvents->Deactivated(); [self showAppMenuOnly]; + + [self invalidateShadow]; [super resignKeyWindow]; } -- (void)windowDidMove:(NSNotification *)notification +- (void)windowDidMove:(NSNotification *_Nonnull)notification { AvnPoint position; @@ -372,6 +407,11 @@ if(cparent != nullptr) { + if(!cparent->IsShown()) + { + return; + } + if(cparent->WindowState() == Maximized) { cparent->SetWindowState(Normal); @@ -389,7 +429,7 @@ return pt; } -- (void)sendEvent:(NSEvent *)event +- (void)sendEvent:(NSEvent *_Nonnull)event { [super sendEvent:event]; @@ -412,8 +452,13 @@ _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } + + if(!_isTransitioningToFullScreen) + { + _parent->BringToFront(); + } } - break; + break; case NSEventTypeMouseEntered: { diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h index ae64a53e7d..3c5010966b 100644 --- a/native/Avalonia.Native/src/OSX/INSWindowHolder.h +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -11,7 +11,7 @@ struct INSWindowHolder { virtual NSWindow* _Nonnull GetNSWindow () = 0; - virtual NSView* _Nonnull GetNSView () = 0; + virtual AvnView* _Nonnull GetNSView () = 0; }; #endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm index 64a8780158..9820a9f052 100644 --- a/native/Avalonia.Native/src/OSX/PopupImpl.mm +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -34,27 +34,16 @@ protected: return NSWindowStyleMaskBorderless; } - virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override - { - START_COM_CALL; - - @autoreleasepool - { - if (Window != nullptr) - { - [Window setContentSize:NSSize{x, y}]; - - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; - } - - return S_OK; - } - } public: virtual bool ShouldTakeFocusOnShow() override { return false; } + + virtual HRESULT Show(bool activate, bool isDialog) override + { + return WindowBaseImpl::Show(activate, true); + } }; @@ -65,4 +54,4 @@ extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); return ptr; } -} \ No newline at end of file +} diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index eff13bcb23..2baf3b09b5 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -16,8 +16,6 @@ class WindowBaseImpl : public virtual ComObject, public virtual IAvnWindowBase, public INSWindowHolder { -private: - NSCursor *cursor; public: FORWARD_IUNKNOWN() @@ -28,23 +26,7 @@ BEGIN_INTERFACE_MAP() virtual ~WindowBaseImpl(); - AutoFitContentView *StandardContainer; - AvnView *View; - NSWindow * Window; - ComPtr BaseEvents; - ComPtr _glContext; - NSObject *renderTarget; - AvnPoint lastPositionSet; - NSSize lastSize; - NSSize lastMinSize; - NSSize lastMaxSize; - AvnMenu* lastMenu; - NSString *_lastTitle; - - bool _shown; - bool _inResize; - - WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl); + WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel = false); virtual HRESULT ObtainNSWindowHandle(void **ret) override; @@ -56,10 +38,12 @@ BEGIN_INTERFACE_MAP() virtual NSWindow *GetNSWindow() override; - virtual NSView *GetNSView() override; + virtual AvnView *GetNSView() override; virtual HRESULT Show(bool activate, bool isDialog) override; + virtual bool IsShown (); + virtual bool ShouldTakeFocusOnShow(); virtual HRESULT Hide() override; @@ -115,6 +99,8 @@ BEGIN_INTERFACE_MAP() virtual bool IsDialog(); id GetWindowProtocol (); + + virtual void BringToFront (); protected: virtual NSWindowStyleMask GetStyle(); @@ -124,7 +110,26 @@ protected: private: void CreateNSWindow (bool isDialog); void CleanNSWindow (); - void InitialiseNSWindow (); + + NSCursor *cursor; + ComPtr _glContext; + bool hasPosition; + NSSize lastSize; + NSSize lastMinSize; + NSSize lastMaxSize; + AvnMenu* lastMenu; + bool _inResize; + +protected: + AvnPoint lastPositionSet; + AutoFitContentView *StandardContainer; + bool _shown; + +public: + NSObject *renderTarget; + NSWindow * Window; + ComPtr BaseEvents; + AvnView *View; }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index db5eb54e3f..f133fa34f6 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -21,7 +21,7 @@ WindowBaseImpl::~WindowBaseImpl() { Window = nullptr; } -WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { +WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl, bool usePanel) { _shown = false; _inResize = false; BaseEvents = events; @@ -30,15 +30,25 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) View = [[AvnView alloc] initWithParent:this]; StandardContainer = [[AutoFitContentView new] initWithContent:View]; - lastPositionSet.X = 100; - lastPositionSet.Y = 100; + lastPositionSet = { 0, 0 }; + hasPosition = false; lastSize = NSSize { 100, 100 }; lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; lastMinSize = NSSize { 0, 0 }; - _lastTitle = @""; - Window = nullptr; lastMenu = nullptr; + + CreateNSWindow(usePanel); + + [Window setContentView:StandardContainer]; + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + + [Window setOpaque:false]; + [Window setHasShadow:true]; } HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { @@ -69,7 +79,7 @@ NSWindow *WindowBaseImpl::GetNSWindow() { return Window; } -NSView *WindowBaseImpl::GetNSView() { +AvnView *WindowBaseImpl::GetNSView() { return View; } @@ -89,13 +99,19 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { - CreateNSWindow(isDialog); - InitialiseNSWindow(); + [Window setContentSize:lastSize]; + + if(hasPosition) + { + SetPosition(lastPositionSet); + } else + { + [Window center]; + } - SetPosition(lastPositionSet); UpdateStyle(); - - [Window setTitle:_lastTitle]; + + [Window invalidateShadow]; if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; @@ -112,6 +128,11 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { } } +bool WindowBaseImpl::IsShown () +{ + return _shown; +} + bool WindowBaseImpl::ShouldTakeFocusOnShow() { return true; } @@ -134,8 +155,6 @@ HRESULT WindowBaseImpl::Hide() { @autoreleasepool { if (Window != nullptr) { [Window orderOut:Window]; - - [GetWindowProtocol() restoreParentWindow]; } return S_OK; @@ -191,9 +210,8 @@ HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) { if (ret == nullptr) return E_POINTER; - auto frame = [View frame]; - ret->Width = frame.size.width; - ret->Height = frame.size.height; + ret->Width = lastSize.width; + ret->Height = lastSize.height; return S_OK; } @@ -206,9 +224,11 @@ HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) { if (ret == nullptr) return E_POINTER; - auto frame = [Window frame]; - ret->Width = frame.size.width; - ret->Height = frame.size.height; + if(Window != nullptr){ + auto frame = [Window frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + } return S_OK; } @@ -278,14 +298,15 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso } @try { - if (!_shown) { - BaseEvents->Resized(AvnSize{x, y}, reason); - } - - lastSize = NSSize {x, y}; - - if(Window != nullptr) { - [Window setContentSize:lastSize]; + if(x != lastSize.width || y != lastSize.height) { + lastSize = NSSize{x, y}; + + if (!_shown) { + BaseEvents->Resized(AvnSize{x, y}, reason); + } else if (Window != nullptr) { + [Window setContentSize:lastSize]; + [Window invalidateShadow]; + } } } @finally { @@ -354,12 +375,17 @@ HRESULT WindowBaseImpl::GetPosition(AvnPoint *ret) { return E_POINTER; } - auto frame = [Window frame]; + if(Window != nullptr) { + auto frame = [Window frame]; - ret->X = frame.origin.x; - ret->Y = frame.origin.y + frame.size.height; + ret->X = frame.origin.x; + ret->Y = frame.origin.y + frame.size.height; - *ret = ConvertPointY(*ret); + *ret = ConvertPointY(*ret); + } else + { + *ret = lastPositionSet; + } return S_OK; } @@ -370,7 +396,11 @@ HRESULT WindowBaseImpl::SetPosition(AvnPoint point) { @autoreleasepool { lastPositionSet = point; - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; + hasPosition = true; + + if(Window != nullptr) { + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; + } return S_OK; } @@ -538,6 +568,8 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) { CleanNSWindow(); Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + + [Window setHidesOnDeactivate:false]; } } else { if (![Window isKindOfClass:[AvnWindow class]]) { @@ -548,35 +580,18 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) { } } -void WindowBaseImpl::InitialiseNSWindow() { - if(Window != nullptr) { - [Window setContentView:StandardContainer]; - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - - [Window setContentSize:lastSize]; - [Window setContentMinSize:lastMinSize]; - [Window setContentMaxSize:lastMaxSize]; - - [Window setOpaque:false]; - - if (lastMenu != nullptr) { - [GetWindowProtocol() applyMenu:lastMenu]; - - if ([Window isKeyWindow]) { - [GetWindowProtocol() showWindowMenuWithAppMenu]; - } - } - } -} - id WindowBaseImpl::GetWindowProtocol() { if(Window == nullptr) { return nullptr; } - return static_cast>(Window); + return (id ) Window; +} + +void WindowBaseImpl::BringToFront() +{ + // do nothing. } extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index a4ee4f447c..b4b1d4e70b 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -8,10 +8,12 @@ #import "WindowBaseImpl.h" #include "IWindowStateChanged.h" +#include class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged { private: + bool _isEnabled; bool _canResize; bool _fullScreenActive; SystemDecorations _decorations; @@ -22,6 +24,8 @@ private: bool _transitioningWindowState; bool _isClientAreaExtended; bool _isDialog; + WindowImpl* _parent; + std::list _children; AvnExtendClientAreaChromeHints _extendClientHints; FORWARD_IUNKNOWN() @@ -88,9 +92,17 @@ BEGIN_INTERFACE_MAP() virtual HRESULT SetWindowState (AvnWindowState state) override; virtual bool IsDialog() override; + + virtual void BringToFront () override; + + bool CanBecomeKeyWindow (); protected: virtual NSWindowStyleMask GetStyle() override; + +private: + void OnInitialiseNSWindow(); + NSString *_lastTitle; }; #endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 7ab2b2b5fc..6db586f3ca 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -10,6 +10,8 @@ #include "WindowProtocol.h" WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { + _isEnabled = true; + _children = std::list(); _isClientAreaExtended = false; _extendClientHints = AvnDefaultChrome; _fullScreenActive = false; @@ -19,10 +21,11 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _inSetWindowState = false; _lastWindowState = Normal; _actualWindowState = Normal; + _lastTitle = @""; + _parent = nullptr; WindowEvents = events; - [Window disableCursorRects]; - [Window setTabbingMode:NSWindowTabbingModeDisallowed]; - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + + OnInitialiseNSWindow(); } void WindowImpl::HideOrShowTrafficLights() { @@ -30,23 +33,27 @@ void WindowImpl::HideOrShowTrafficLights() { return; } - for (id subview in Window.contentView.superview.subviews) { - if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { - NSView *titlebarView = [subview subviews][0]; - for (id button in titlebarView.subviews) { - if ([button isKindOfClass:[NSButton class]]) { - if (_isClientAreaExtended) { - auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull; + + [[Window standardWindowButton:NSWindowCloseButton] setHidden:!hasTrafficLights]; + [[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:!hasTrafficLights]; + [[Window standardWindowButton:NSWindowZoomButton] setHidden:!hasTrafficLights]; +} - [button setHidden:!wantsChrome]; - } else { - [button setHidden:(_decorations != SystemDecorationsFull)]; - } +void WindowImpl::OnInitialiseNSWindow(){ + [GetWindowProtocol() setCanBecomeKeyWindow:true]; + + [Window disableCursorRects]; + [Window setTabbingMode:NSWindowTabbingModeDisallowed]; + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - [button setWantsLayer:true]; - } - } - } + [Window setTitle:_lastTitle]; + + if(_isClientAreaExtended) + { + [GetWindowProtocol() setIsExtended:true]; + SetExtendClientArea(true); } } @@ -56,19 +63,8 @@ HRESULT WindowImpl::Show(bool activate, bool isDialog) { @autoreleasepool { _isDialog = isDialog; - bool created = Window == nullptr; - WindowBaseImpl::Show(activate, isDialog); - if(created) - { - if(_isClientAreaExtended) - { - [GetWindowProtocol() setIsExtended:true]; - SetExtendClientArea(true); - } - } - HideOrShowTrafficLights(); return SetWindowState(_lastWindowState); @@ -79,7 +75,9 @@ HRESULT WindowImpl::SetEnabled(bool enable) { START_COM_CALL; @autoreleasepool { + _isEnabled = enable; [GetWindowProtocol() setEnabled:enable]; + UpdateStyle(); return S_OK; } } @@ -88,26 +86,68 @@ HRESULT WindowImpl::SetParent(IAvnWindow *parent) { START_COM_CALL; @autoreleasepool { - if (parent == nullptr) - return E_POINTER; + if(_parent != nullptr) + { + _parent->_children.remove(this); + + _parent->BringToFront(); + } auto cparent = dynamic_cast(parent); - if (cparent == nullptr) - return E_INVALIDARG; - - // If one tries to show a child window with a minimized parent window, then the parent window will be - // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive - // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. - if (cparent->WindowState() == Minimized) - cparent->SetWindowState(Normal); + + _parent = cparent; + + if(_parent != nullptr && Window != nullptr){ + // If one tries to show a child window with a minimized parent window, then the parent window will be + // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive + // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. + if (cparent->WindowState() == Minimized) + cparent->SetWindowState(Normal); + + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; + + cparent->_children.push_back(this); + + UpdateStyle(); + } - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; - [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; + return S_OK; + } +} - UpdateStyle(); +void WindowImpl::BringToFront() +{ + if(Window != nullptr) + { + if(IsDialog()) + { + Activate(); + } + else + { + [Window orderFront:nullptr]; + } + + [Window invalidateShadow]; + + for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) + { + (*iterator)->BringToFront(); + } + } +} - return S_OK; +bool WindowImpl::CanBecomeKeyWindow() +{ + for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) + { + if((*iterator)->IsDialog()) + { + return false; + } } + + return true; } void WindowImpl::StartStateTransition() { @@ -430,6 +470,9 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) { START_COM_CALL; @autoreleasepool { + auto currentState = _actualWindowState; + _lastWindowState = state; + if (Window == nullptr) { return S_OK; } @@ -440,9 +483,6 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) { _inSetWindowState = true; - auto currentState = _actualWindowState; - _lastWindowState = state; - if (currentState == Normal) { _preZoomSize = [Window frame]; } @@ -521,7 +561,12 @@ bool WindowImpl::IsDialog() { } NSWindowStyleMask WindowImpl::GetStyle() { - unsigned long s = this->_isDialog ? NSWindowStyleMaskUtilityWindow : NSWindowStyleMaskBorderless; + unsigned long s = NSWindowStyleMaskBorderless; + + if(_actualWindowState == FullScreen) + { + s |= NSWindowStyleMaskFullScreen; + } switch (_decorations) { case SystemDecorationsNone: @@ -533,15 +578,15 @@ NSWindowStyleMask WindowImpl::GetStyle() { break; case SystemDecorationsFull: - s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless; + s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable; - if (_canResize) { + if (_canResize && _isEnabled) { s = s | NSWindowStyleMaskResizable; } break; } - if ([Window parentWindow] == nullptr) { + if (!IsDialog()) { s |= NSWindowStyleMaskMiniaturizable; } diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index 92194706de..cb5f86bdb9 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -11,7 +11,6 @@ @protocol AvnWindowProtocol -(void) pollModalSession: (NSModalSession _Nonnull) session; --(void) restoreParentWindow; -(bool) shouldTryToHandleEvents; -(void) setEnabled: (bool) enable; -(void) showAppMenuOnly; @@ -22,4 +21,6 @@ -(void) setIsExtended:(bool)value; -(void) disconnectParent; -(bool) isDialog; -@end \ No newline at end of file + +-(void) setCanBecomeKeyWindow:(bool)value; +@end diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 14f1f6888c..a15d0c9601 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -82,6 +82,17 @@ ComPtr _events; _isHandlingSendEvent = oldHandling; } } + +// This is needed for certain embedded controls DO NOT REMOVE.. +- (BOOL) isHandlingSendEvent +{ + return _isHandlingSendEvent; +} + +- (void)setHandlingSendEvent:(BOOL)handlingSendEvent +{ + _isHandlingSendEvent = handlingSendEvent; +} @end extern void InitializeAvnApp(IAvnApplicationEvents* events) diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index f0f677b844..9fcb9d6b7f 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -221,7 +221,6 @@ partial class Build : NukeBuild RunCoreTest("Avalonia.Markup.Xaml.UnitTests"); RunCoreTest("Avalonia.Skia.UnitTests"); RunCoreTest("Avalonia.ReactiveUI.UnitTests"); - RunCoreTest("Avalonia.PlatformSupport.UnitTests"); }); Target RunRenderTests => _ => _ diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 52b60b7d0f..b2c58e2292 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -8,11 +8,10 @@ False CS0649;CS0169 - + - diff --git a/readme.md b/readme.md index 1cdaf3b8f8..1009e86c29 100644 --- a/readme.md +++ b/readme.md @@ -70,11 +70,15 @@ For more information see the [.NET Foundation Code of Conduct](https://dotnetfou Avalonia is licenced under the [MIT licence](licence.md). -## Support Avalonia +## Donate -**BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx +Donating to the project is a fantastic way to thank our valued contributors for their hard work. Your donations are shared among our community and awarded for significant contributions. + +If you need support see Commercial Support section below. + +Donate with BTC or use [Open Collective](https://opencollective.com/avalonia). -This will be shared with the community and awarded for significant contributions. +**BTC**: bc1q05wx78qemgy9x6ytl5ljk2xrt00yqargyjm8gx ### Backers @@ -98,6 +102,11 @@ Support this project by becoming a sponsor. Your logo will show up here with a l +## Commercial Support + +We have a range of [support plans available](https://avaloniaui.net/support.html) for those looking to partner with the creators of Avalonia, enabling access to the best support at every step of the development process. + +*Please note that donations are not considered payment for commercial support agreements. Please contact us to discuss your needs first. [team@avaloniaui.net](mailto://team@avaloniaui.net)* ## .NET Foundation This project is supported by the [.NET Foundation](https://dotnetfoundation.org). diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj index 2c6ff74e5e..bd6054327f 100644 --- a/samples/BindingDemo/BindingDemo.csproj +++ b/samples/BindingDemo/BindingDemo.csproj @@ -5,6 +5,7 @@ + diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 04c67e84e8..e52430f50b 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -9,39 +9,37 @@ 1.0 apk true + android-arm64;android-x64 - - - Resources\drawable\Icon.png - - True + True - True - True - - - False - False + + + True + no-write-symbols,nodebug + Hybrid + True - - True + + True + True - - + + - \ No newline at end of file + diff --git a/samples/ControlCatalog.Android/EmbedSample.Android.cs b/samples/ControlCatalog.Android/EmbedSample.Android.cs new file mode 100644 index 0000000000..250121fc53 --- /dev/null +++ b/samples/ControlCatalog.Android/EmbedSample.Android.cs @@ -0,0 +1,35 @@ +using System; +using Avalonia.Platform; +using Avalonia.Android; +using ControlCatalog.Pages; + +namespace ControlCatalog.Android; + +public class EmbedSampleAndroid : INativeDemoControl +{ + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) + { + var parentContext = (parent as AndroidViewControlHandle)?.View.Context + ?? global::Android.App.Application.Context; + + if (isSecond) + { + var webView = new global::Android.Webkit.WebView(parentContext); + webView.LoadUrl("https://www.android.com/"); + + return new AndroidViewControlHandle(webView); + } + else + { + var button = new global::Android.Widget.Button(parentContext) { Text = "Hello world" }; + var clickCount = 0; + button.Click += (sender, args) => + { + clickCount++; + button.Text = $"Click count {clickCount}"; + }; + + return new AndroidViewControlHandle(button); + } + } +} diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs index 44290d9816..33ca511340 100644 --- a/samples/ControlCatalog.Android/MainActivity.cs +++ b/samples/ControlCatalog.Android/MainActivity.cs @@ -10,7 +10,11 @@ namespace ControlCatalog.Android { protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) { - return base.CustomizeAppBuilder(builder); + return base.CustomizeAppBuilder(builder) + .AfterSetup(_ => + { + Pages.EmbedSample.Implementation = new EmbedSampleAndroid(); + }); } } } diff --git a/samples/ControlCatalog.Android/Properties/AndroidManifest.xml b/samples/ControlCatalog.Android/Properties/AndroidManifest.xml index aa570ec504..6f551d2b01 100644 --- a/samples/ControlCatalog.Android/Properties/AndroidManifest.xml +++ b/samples/ControlCatalog.Android/Properties/AndroidManifest.xml @@ -1,4 +1,5 @@  - + + diff --git a/samples/ControlCatalog.Android/environment.device.txt b/samples/ControlCatalog.Android/environment.device.txt new file mode 100644 index 0000000000..107d68ca1b --- /dev/null +++ b/samples/ControlCatalog.Android/environment.device.txt @@ -0,0 +1 @@ +DOTNET_DiagnosticPorts=127.0.0.1:9000,suspend diff --git a/samples/ControlCatalog.Android/environment.emulator.txt b/samples/ControlCatalog.Android/environment.emulator.txt new file mode 100644 index 0000000000..299a0ec30b --- /dev/null +++ b/samples/ControlCatalog.Android/environment.emulator.txt @@ -0,0 +1 @@ +DOTNET_DiagnosticPorts=10.0.2.2:9001,suspend diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 2b45ac1508..0667644643 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -4,6 +4,7 @@ WinExe net6.0 true + true @@ -12,6 +13,16 @@ 7.0.0-* + + + + + + + PreserveNewest + + + @@ -20,6 +31,8 @@ + + @@ -32,6 +45,7 @@ en + app.manifest diff --git a/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs b/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs new file mode 100644 index 0000000000..521d3674eb --- /dev/null +++ b/samples/ControlCatalog.NetCore/NativeControls/Gtk/EmbedSample.Gtk.cs @@ -0,0 +1,35 @@ +using System.IO; +using System.Diagnostics; +using Avalonia.Platform; +using Avalonia.Controls.Platform; +using System; +using ControlCatalog.Pages; + +namespace ControlCatalog.NetCore; + +public class EmbedSampleGtk : INativeDemoControl +{ + private Process _mplayer; + + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) + { + if (isSecond) + { + var chooser = GtkHelper.CreateGtkFileChooser(parent.Handle); + if (chooser != null) + return chooser; + } + + var control = createDefault(); + var nodes = Path.GetFullPath(Path.Combine(typeof(EmbedSample).Assembly.GetModules()[0].FullyQualifiedName, + "..", + "nodes.mp4")); + _mplayer = Process.Start(new ProcessStartInfo("mplayer", + $"-vo x11 -zoom -loop 0 -wid {control.Handle.ToInt64()} \"{nodes}\"") + { + UseShellExecute = false, + + }); + return control; + } +} diff --git a/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs b/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs new file mode 100644 index 0000000000..456f77a44d --- /dev/null +++ b/samples/ControlCatalog.NetCore/NativeControls/Gtk/GtkHelper.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; +using Avalonia.Controls.Platform; +using Avalonia.Platform.Interop; +using Avalonia.X11.NativeDialogs; +using static Avalonia.X11.NativeDialogs.Gtk; +using static Avalonia.X11.NativeDialogs.Glib; + +namespace ControlCatalog.NetCore; + +internal class GtkHelper +{ + private static Task s_gtkTask; + + class FileChooser : INativeControlHostDestroyableControlHandle + { + private readonly IntPtr _widget; + + public FileChooser(IntPtr widget, IntPtr xid) + { + _widget = widget; + Handle = xid; + } + + public IntPtr Handle { get; } + public string HandleDescriptor => "XID"; + + public void Destroy() + { + RunOnGlibThread(() => + { + gtk_widget_destroy(_widget); + return 0; + }).Wait(); + } + } + + + public static INativeControlHostDestroyableControlHandle CreateGtkFileChooser(IntPtr parentXid) + { + if (s_gtkTask == null) + s_gtkTask = StartGtk(); + if (!s_gtkTask.Result) + return null; + return RunOnGlibThread(() => + { + using (var title = new Utf8Buffer("Embedded")) + { + var widget = gtk_file_chooser_dialog_new(title, IntPtr.Zero, GtkFileChooserAction.SelectFolder, + IntPtr.Zero); + gtk_widget_realize(widget); + var xid = gdk_x11_window_get_xid(gtk_widget_get_window(widget)); + gtk_window_present(widget); + return new FileChooser(widget, xid); + } + }).Result; + } +} diff --git a/samples/interop/NativeEmbedSample/nodes-license.md b/samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md similarity index 100% rename from samples/interop/NativeEmbedSample/nodes-license.md rename to samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes-license.md diff --git a/samples/interop/NativeEmbedSample/nodes.mp4 b/samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4 similarity index 100% rename from samples/interop/NativeEmbedSample/nodes.mp4 rename to samples/ControlCatalog.NetCore/NativeControls/Gtk/nodes.mp4 diff --git a/samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs b/samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs new file mode 100644 index 0000000000..7967c9c073 --- /dev/null +++ b/samples/ControlCatalog.NetCore/NativeControls/Mac/EmbedSample.Mac.cs @@ -0,0 +1,29 @@ +using System; + +using Avalonia.Platform; +using Avalonia.Threading; + +using ControlCatalog.Pages; + +using MonoMac.Foundation; +using MonoMac.WebKit; + +namespace ControlCatalog.NetCore; + +public class EmbedSampleMac : INativeDemoControl +{ + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) + { + // Note: We are using MonoMac for example purposes + // It shouldn't be used in production apps + MacHelper.EnsureInitialized(); + + var webView = new WebView(); + Dispatcher.UIThread.Post(() => + { + webView.MainFrame.LoadRequest(new NSUrlRequest(new NSUrl( + isSecond ? "https://bing.com" : "https://google.com/"))); + }); + return new MacOSViewHandle(webView); + } +} diff --git a/samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs b/samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs new file mode 100644 index 0000000000..5b3bc9abf1 --- /dev/null +++ b/samples/ControlCatalog.NetCore/NativeControls/Mac/MacHelper.cs @@ -0,0 +1,38 @@ +using System; + +using Avalonia.Controls.Platform; +using MonoMac.AppKit; + +namespace ControlCatalog.NetCore; + +internal class MacHelper +{ + private static bool _isInitialized; + + public static void EnsureInitialized() + { + if (_isInitialized) + return; + _isInitialized = true; + NSApplication.Init(); + } +} + +internal class MacOSViewHandle : INativeControlHostDestroyableControlHandle +{ + private NSView _view; + + public MacOSViewHandle(NSView view) + { + _view = view; + } + + public IntPtr Handle => _view?.Handle ?? IntPtr.Zero; + public string HandleDescriptor => "NSView"; + + public void Destroy() + { + _view.Dispose(); + _view = null; + } +} diff --git a/samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs b/samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs new file mode 100644 index 0000000000..77982db0ca --- /dev/null +++ b/samples/ControlCatalog.NetCore/NativeControls/Win/EmbedSample.Win.cs @@ -0,0 +1,45 @@ +using System; +using System.Text; + +using Avalonia.Controls.Platform; +using Avalonia.Platform; + +using ControlCatalog.Pages; + +namespace ControlCatalog.NetCore; + +public class EmbedSampleWin : INativeDemoControl +{ + private const string RichText = + @"{\rtf1\ansi\ansicpg1251\deff0\nouicompat\deflang1049{\fonttbl{\f0\fnil\fcharset0 Calibri;}} +{\colortbl ;\red255\green0\blue0;\red0\green77\blue187;\red0\green176\blue80;\red155\green0\blue211;\red247\green150\blue70;\red75\green172\blue198;} +{\*\generator Riched20 6.3.9600}\viewkind4\uc1 +\pard\sa200\sl276\slmult1\f0\fs22\lang9 I \i am\i0 a \cf1\b Rich Text \cf0\b0\fs24 control\cf2\fs28 !\cf3\fs32 !\cf4\fs36 !\cf1\fs40 !\cf5\fs44 !\cf6\fs48 !\cf0\fs44\par +}"; + + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) + { + WinApi.LoadLibrary("Msftedit.dll"); + var handle = WinApi.CreateWindowEx(0, "RICHEDIT50W", + @"Rich Edit", + 0x800000 | 0x10000000 | 0x40000000 | 0x800000 | 0x10000 | 0x0004, 0, 0, 1, 1, parent.Handle, + IntPtr.Zero, WinApi.GetModuleHandle(null), IntPtr.Zero); + var st = new WinApi.SETTEXTEX { Codepage = 65001, Flags = 0x00000008 }; + var text = RichText.Replace("", isSecond ? "\\qr " : ""); + var bytes = Encoding.UTF8.GetBytes(text); + WinApi.SendMessage(handle, 0x0400 + 97, ref st, bytes); + return new Win32WindowControlHandle(handle, "HWND"); + } +} + +internal class Win32WindowControlHandle : PlatformHandle, INativeControlHostDestroyableControlHandle +{ + public Win32WindowControlHandle(IntPtr handle, string descriptor) : base(handle, descriptor) + { + } + + public void Destroy() + { + _ = WinApi.DestroyWindow(Handle); + } +} diff --git a/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs b/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs new file mode 100644 index 0000000000..47d368f7a4 --- /dev/null +++ b/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs @@ -0,0 +1,73 @@ +using System; +using System.Runtime.InteropServices; + +namespace ControlCatalog.NetCore; + +internal unsafe class WinApi +{ + public enum CommonControls : uint + { + ICC_LISTVIEW_CLASSES = 0x00000001, // listview, header + ICC_TREEVIEW_CLASSES = 0x00000002, // treeview, tooltips + ICC_BAR_CLASSES = 0x00000004, // toolbar, statusbar, trackbar, tooltips + ICC_TAB_CLASSES = 0x00000008, // tab, tooltips + ICC_UPDOWN_CLASS = 0x00000010, // updown + ICC_PROGRESS_CLASS = 0x00000020, // progress + ICC_HOTKEY_CLASS = 0x00000040, // hotkey + ICC_ANIMATE_CLASS = 0x00000080, // animate + ICC_WIN95_CLASSES = 0x000000FF, + ICC_DATE_CLASSES = 0x00000100, // month picker, date picker, time picker, updown + ICC_USEREX_CLASSES = 0x00000200, // comboex + ICC_COOL_CLASSES = 0x00000400, // rebar (coolbar) control + ICC_INTERNET_CLASSES = 0x00000800, + ICC_PAGESCROLLER_CLASS = 0x00001000, // page scroller + ICC_NATIVEFNTCTL_CLASS = 0x00002000, // native font control + ICC_STANDARD_CLASSES = 0x00004000, + ICC_LINK_CLASS = 0x00008000 + } + + [StructLayout(LayoutKind.Sequential)] + public struct INITCOMMONCONTROLSEX + { + public int dwSize; + public uint dwICC; + } + + [DllImport("Comctl32.dll")] + public static extern void InitCommonControlsEx(ref INITCOMMONCONTROLSEX init); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool DestroyWindow(IntPtr hwnd); + + [DllImport("kernel32.dll")] + public static extern IntPtr LoadLibrary(string lib); + + + [DllImport("kernel32.dll")] + public static extern IntPtr GetModuleHandle(string lpModuleName); + + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr CreateWindowEx( + int dwExStyle, + string lpClassName, + string lpWindowName, + uint dwStyle, + int x, + int y, + int nWidth, + int nHeight, + IntPtr hWndParent, + IntPtr hMenu, + IntPtr hInstance, + IntPtr lpParam); + + [StructLayout(LayoutKind.Sequential)] + public struct SETTEXTEX + { + public uint Flags; + public uint Codepage; + } + + [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")] + public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, ref SETTEXTEX wParam, byte[] lParam); +} diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 4464413e63..fd080cfc5b 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -7,11 +7,12 @@ using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Dialogs; using Avalonia.Headless; using Avalonia.LogicalTree; using Avalonia.Threading; +using ControlCatalog.Pages; + namespace ControlCatalog.NetCore { static class Program @@ -123,6 +124,11 @@ namespace ControlCatalog.NetCore { StartupScreenIndex = 1, }); + + EmbedSample.Implementation = OperatingSystem.IsWindows() ? (INativeDemoControl)new EmbedSampleWin() + : OperatingSystem.IsMacOS() ? new EmbedSampleMac() + : OperatingSystem.IsLinux() ? new EmbedSampleGtk() + : null; }) .LogToTrace(); diff --git a/samples/ControlCatalog.NetCore/app.manifest b/samples/ControlCatalog.NetCore/app.manifest new file mode 100644 index 0000000000..db90057191 --- /dev/null +++ b/samples/ControlCatalog.NetCore/app.manifest @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ControlCatalog.Web/App.razor.cs b/samples/ControlCatalog.Web/App.razor.cs index a150824ac3..c0b7ddbe1e 100644 --- a/samples/ControlCatalog.Web/App.razor.cs +++ b/samples/ControlCatalog.Web/App.razor.cs @@ -7,6 +7,10 @@ public partial class App protected override void OnParametersSet() { WebAppBuilder.Configure() + .AfterSetup(_ => + { + ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb(); + }) .SetupWithSingleViewLifetime(); base.OnParametersSet(); diff --git a/samples/ControlCatalog.Web/EmbedSample.Browser.cs b/samples/ControlCatalog.Web/EmbedSample.Browser.cs new file mode 100644 index 0000000000..5fe14409de --- /dev/null +++ b/samples/ControlCatalog.Web/EmbedSample.Browser.cs @@ -0,0 +1,34 @@ +using System; + +using Avalonia; +using Avalonia.Platform; +using Avalonia.Web.Blazor; + +using ControlCatalog.Pages; + +using Microsoft.JSInterop; + +namespace ControlCatalog.Web; + +public class EmbedSampleWeb : INativeDemoControl +{ + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) + { + var runtime = AvaloniaLocator.Current.GetRequiredService(); + + if (isSecond) + { + var iframe = runtime.Invoke("document.createElement", "iframe"); + iframe.InvokeVoid("setAttribute", "src", "https://www.youtube.com/embed/kZCIporjJ70"); + + return new JSObjectControlHandle(iframe); + } + else + { + // window.createAppButton source is defined in "app.js" file. + var button = runtime.Invoke("window.createAppButton"); + + return new JSObjectControlHandle(button); + } + } +} diff --git a/samples/ControlCatalog.Web/Shared/MainLayout.razor.css b/samples/ControlCatalog.Web/Shared/MainLayout.razor.css deleted file mode 100644 index 43c355a47a..0000000000 --- a/samples/ControlCatalog.Web/Shared/MainLayout.razor.css +++ /dev/null @@ -1,70 +0,0 @@ -.page { - position: relative; - display: flex; - flex-direction: column; -} - -.main { - flex: 1; -} - -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - -.top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; - justify-content: flex-end; - height: 3.5rem; - display: flex; - align-items: center; -} - - .top-row ::deep a, .top-row .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - } - - .top-row a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } - -@media (max-width: 640.98px) { - .top-row:not(.auth) { - display: none; - } - - .top-row.auth { - justify-content: space-between; - } - - .top-row a, .top-row .btn-link { - margin-left: 0; - } -} - -@media (min-width: 641px) { - .page { - flex-direction: row; - } - - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; - } - - .top-row { - position: sticky; - top: 0; - z-index: 1; - } - - .main > div { - padding-left: 2rem !important; - padding-right: 1.5rem !important; - } -} diff --git a/samples/ControlCatalog.Web/wwwroot/css/app.css b/samples/ControlCatalog.Web/wwwroot/css/app.css index d2a8dc525c..49ca14e162 100644 --- a/samples/ControlCatalog.Web/wwwroot/css/app.css +++ b/samples/ControlCatalog.Web/wwwroot/css/app.css @@ -44,47 +44,13 @@ a, .btn-link { z-index: 1000; } - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } - -.canvas-container { - opacity:1; - background-color:#ccc; - position:fixed; - width:100%; - height:100%; - top:0px; - left:0px; - z-index:500; -} - -canvas -{ - opacity:1; - background-color:#ccc; - position:fixed; - width:100%; - height:100%; - top:0px; - left:0px; - z-index:500; +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; } #app, .page { height: 100%; } - -.overlay{ - opacity:0.0; - background-color:#ccc; - position:fixed; - width:100vw; - height:100vh; - top:0px; - left:0px; - z-index:1000; -} diff --git a/samples/ControlCatalog.Web/wwwroot/js/app.js b/samples/ControlCatalog.Web/wwwroot/js/app.js index 5f282702bb..29697661a6 100644 --- a/samples/ControlCatalog.Web/wwwroot/js/app.js +++ b/samples/ControlCatalog.Web/wwwroot/js/app.js @@ -1 +1,10 @@ - \ No newline at end of file +window.createAppButton = function () { + var button = document.createElement('button'); + button.innerText = 'Hello world'; + var clickCount = 0; + button.onclick = () => { + clickCount++; + button.innerText = 'Click count ' + clickCount; + }; + return button; +} diff --git a/samples/ControlCatalog.iOS/AppDelegate.cs b/samples/ControlCatalog.iOS/AppDelegate.cs index f1c2241003..f8caffed14 100644 --- a/samples/ControlCatalog.iOS/AppDelegate.cs +++ b/samples/ControlCatalog.iOS/AppDelegate.cs @@ -13,6 +13,13 @@ namespace ControlCatalog [Register("AppDelegate")] public partial class AppDelegate : AvaloniaAppDelegate { - + protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) + { + return base.CustomizeAppBuilder(builder) + .AfterSetup(_ => + { + Pages.EmbedSample.Implementation = new EmbedSampleIOS(); + }); + } } } diff --git a/samples/ControlCatalog.iOS/EmbedSample.iOS.cs b/samples/ControlCatalog.iOS/EmbedSample.iOS.cs new file mode 100644 index 0000000000..ad86d2b578 --- /dev/null +++ b/samples/ControlCatalog.iOS/EmbedSample.iOS.cs @@ -0,0 +1,38 @@ +using System; +using Avalonia.Platform; +using CoreGraphics; +using Foundation; +using UIKit; +using WebKit; +using Avalonia.iOS; +using ControlCatalog.Pages; + +namespace ControlCatalog; + +public class EmbedSampleIOS : INativeDemoControl +{ + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) + { + if (isSecond) + { + var webView = new WKWebView(CGRect.Empty, new WKWebViewConfiguration()); + webView.LoadRequest(new NSUrlRequest(new NSUrl("https://www.apple.com/"))); + + return new UIViewControlHandle(webView); + } + else + { + var button = new UIButton(); + var clickCount = 0; + button.SetTitle("Hello world", UIControlState.Normal); + button.BackgroundColor = UIColor.Blue; + button.AddTarget((_, _) => + { + clickCount++; + button.SetTitle($"Click count {clickCount}", UIControlState.Normal); + }, UIControlEvent.TouchDown); + + return new UIViewControlHandle(button); + } + } +} diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 7cbd8a3f9c..903c849834 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,7 +1,8 @@  netstandard2.0 - true + true + enable @@ -13,6 +14,9 @@ + + + @@ -25,9 +29,23 @@ + + + + + MSBuild:Compile + + + + + + %(Filename) + + + diff --git a/samples/ControlCatalog/Converter/MathSubtractConverter.cs b/samples/ControlCatalog/Converter/MathSubtractConverter.cs index 009a56c0ec..6f3c99abe8 100644 --- a/samples/ControlCatalog/Converter/MathSubtractConverter.cs +++ b/samples/ControlCatalog/Converter/MathSubtractConverter.cs @@ -6,12 +6,16 @@ namespace ControlCatalog.Converter; public class MathSubtractConverter : IValueConverter { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { - return (double)value - (double)parameter; + if (value is double dv && parameter is double dp) + { + return dv - dp; + } + return double.NaN; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotSupportedException(); } diff --git a/samples/ControlCatalog/DecoratedWindow.xaml.cs b/samples/ControlCatalog/DecoratedWindow.xaml.cs index a1383b9107..ed30b7f491 100644 --- a/samples/ControlCatalog/DecoratedWindow.xaml.cs +++ b/samples/ControlCatalog/DecoratedWindow.xaml.cs @@ -15,7 +15,7 @@ namespace ControlCatalog void SetupSide(string name, StandardCursorType cursor, WindowEdge edge) { - var ctl = this.FindControl(name); + var ctl = this.Get(name); ctl.Cursor = new Cursor(cursor); ctl.PointerPressed += (i, e) => { @@ -26,7 +26,7 @@ namespace ControlCatalog private void InitializeComponent() { AvaloniaXamlLoader.Load(this); - this.FindControl("TitleBar").PointerPressed += (i, e) => + this.Get("TitleBar").PointerPressed += (i, e) => { PlatformImpl?.BeginMoveDrag(e); }; @@ -38,12 +38,12 @@ namespace ControlCatalog SetupSide("TopRight", StandardCursorType.TopRightCorner, WindowEdge.NorthEast); SetupSide("BottomLeft", StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest); SetupSide("BottomRight", StandardCursorType.BottomRightCorner, WindowEdge.SouthEast); - this.FindControl - - - - - Text - - - Tooltip - - - - - - - - - Visible - - - - - - - - Visible - - - - - - diff --git a/samples/interop/NativeEmbedSample/MainWindow.xaml.cs b/samples/interop/NativeEmbedSample/MainWindow.xaml.cs deleted file mode 100644 index 4324aa2762..0000000000 --- a/samples/interop/NativeEmbedSample/MainWindow.xaml.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Interactivity; -using Avalonia.Markup.Xaml; - -namespace NativeEmbedSample -{ - public class MainWindow : Window - { - public MainWindow() - { - AvaloniaXamlLoader.Load(this); - this.AttachDevTools(); - } - - public async void ShowPopupDelay(object sender, RoutedEventArgs args) - { - await Task.Delay(3000); - ShowPopup(sender, args); - } - - public void ShowPopup(object sender, RoutedEventArgs args) - { - - new ContextMenu() - { - Items = new List - { - new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" } - } - }.Open((Control)sender); - } - } -} diff --git a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj deleted file mode 100644 index c25442b52c..0000000000 --- a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - Exe - netcoreapp2.0 - true - true - - - - - - - - - - - Designer - - - - PreserveNewest - - - - - - - - diff --git a/samples/interop/NativeEmbedSample/Program.cs b/samples/interop/NativeEmbedSample/Program.cs deleted file mode 100644 index baa7837667..0000000000 --- a/samples/interop/NativeEmbedSample/Program.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Avalonia; - -namespace NativeEmbedSample -{ - class Program - { - static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() - .With(new AvaloniaNativePlatformOptions() - { - }) - .UsePlatformDetect(); - - } -} diff --git a/samples/interop/NativeEmbedSample/WinApi.cs b/samples/interop/NativeEmbedSample/WinApi.cs deleted file mode 100644 index 8e5bcdf49e..0000000000 --- a/samples/interop/NativeEmbedSample/WinApi.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace NativeEmbedSample -{ - public unsafe class WinApi - { - public enum CommonControls : uint - { - ICC_LISTVIEW_CLASSES = 0x00000001, // listview, header - ICC_TREEVIEW_CLASSES = 0x00000002, // treeview, tooltips - ICC_BAR_CLASSES = 0x00000004, // toolbar, statusbar, trackbar, tooltips - ICC_TAB_CLASSES = 0x00000008, // tab, tooltips - ICC_UPDOWN_CLASS = 0x00000010, // updown - ICC_PROGRESS_CLASS = 0x00000020, // progress - ICC_HOTKEY_CLASS = 0x00000040, // hotkey - ICC_ANIMATE_CLASS = 0x00000080, // animate - ICC_WIN95_CLASSES = 0x000000FF, - ICC_DATE_CLASSES = 0x00000100, // month picker, date picker, time picker, updown - ICC_USEREX_CLASSES = 0x00000200, // comboex - ICC_COOL_CLASSES = 0x00000400, // rebar (coolbar) control - ICC_INTERNET_CLASSES = 0x00000800, - ICC_PAGESCROLLER_CLASS = 0x00001000, // page scroller - ICC_NATIVEFNTCTL_CLASS = 0x00002000, // native font control - ICC_STANDARD_CLASSES = 0x00004000, - ICC_LINK_CLASS = 0x00008000 - } - - [StructLayout(LayoutKind.Sequential)] - public struct INITCOMMONCONTROLSEX - { - public int dwSize; - public uint dwICC; - } - - [DllImport("Comctl32.dll")] - public static extern void InitCommonControlsEx(ref INITCOMMONCONTROLSEX init); - - [DllImport("user32.dll", SetLastError = true)] - public static extern bool DestroyWindow(IntPtr hwnd); - - [DllImport("kernel32.dll")] - public static extern IntPtr LoadLibrary(string lib); - - - [DllImport("kernel32.dll")] - public static extern IntPtr GetModuleHandle(string lpModuleName); - - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr CreateWindowEx( - int dwExStyle, - string lpClassName, - string lpWindowName, - uint dwStyle, - int x, - int y, - int nWidth, - int nHeight, - IntPtr hWndParent, - IntPtr hMenu, - IntPtr hInstance, - IntPtr lpParam); - - [StructLayout(LayoutKind.Sequential)] - public struct SETTEXTEX - { - public uint Flags; - public uint Codepage; - } - - [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")] - public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, ref SETTEXTEX wParam, byte[] lParam); - } -} diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index 61aa6ce946..c7dd896cb8 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -61,8 +61,6 @@ namespace Avalonia.Android .Bind().ToConstant(new RenderLoop()) .Bind().ToSingleton(); - SkiaPlatform.Initialize(); - if (options.UseGpu) { EglPlatformOpenGlInterface.TryInitialize(); diff --git a/src/Android/Avalonia.Android/AndroidViewControlHandle.cs b/src/Android/Avalonia.Android/AndroidViewControlHandle.cs new file mode 100644 index 0000000000..e999d198c6 --- /dev/null +++ b/src/Android/Avalonia.Android/AndroidViewControlHandle.cs @@ -0,0 +1,32 @@ +#nullable enable + +using System; + +using Android.Views; + +using Avalonia.Controls.Platform; +using Avalonia.Platform; + +namespace Avalonia.Android +{ + public class AndroidViewControlHandle : INativeControlHostDestroyableControlHandle + { + internal const string AndroidDescriptor = "JavaObjectHandle"; + + public AndroidViewControlHandle(View view) + { + View = view; + } + + public View View { get; } + + public string HandleDescriptor => AndroidDescriptor; + + IntPtr IPlatformHandle.Handle => View.Handle; + + public void Destroy() + { + View?.Dispose(); + } + } +} diff --git a/src/Android/Avalonia.Android/AvaloniaActivity.cs b/src/Android/Avalonia.Android/AvaloniaActivity.cs index f5d620a97a..f3692d33d7 100644 --- a/src/Android/Avalonia.Android/AvaloniaActivity.cs +++ b/src/Android/Avalonia.Android/AvaloniaActivity.cs @@ -31,21 +31,22 @@ namespace Avalonia.Android CustomizeAppBuilder(builder); - View = new AvaloniaView(this); - SetContentView(View); var lifetime = new SingleViewLifetime(); - lifetime.View = View; builder.AfterSetup(x => { _viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel; + View = new AvaloniaView(this); if (_viewModel.Content != null) { View.Content = _viewModel.Content; } + SetContentView(View); + lifetime.View = View; + View.Prepare(); }); diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs index 8177cf1f69..94e863210b 100644 --- a/src/Android/Avalonia.Android/AvaloniaView.cs +++ b/src/Android/Avalonia.Android/AvaloniaView.cs @@ -15,13 +15,12 @@ namespace Avalonia.Android private EmbeddableControlRoot _root; private readonly ViewImpl _view; - private IDisposable? _timerSubscription; + private IDisposable _timerSubscription; public AvaloniaView(Context context) : base(context) { - _view = new ViewImpl(context); + _view = new ViewImpl(this); AddView(_view.View); - } internal void Prepare () @@ -30,6 +29,8 @@ namespace Avalonia.Android _root.Prepare(); } + internal TopLevelImpl TopLevelImpl => _view; + public object Content { get { return _root.Content; } @@ -73,7 +74,7 @@ namespace Avalonia.Android class ViewImpl : TopLevelImpl { - public ViewImpl(Context context) : base(context) + public ViewImpl(AvaloniaView avaloniaView) : base(avaloniaView) { View.Focusable = true; View.FocusChange += ViewImpl_FocusChange; diff --git a/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs new file mode 100644 index 0000000000..4738bd86f9 --- /dev/null +++ b/src/Android/Avalonia.Android/Platform/AndroidNativeControlHostImpl.cs @@ -0,0 +1,139 @@ +#nullable enable + +using System; +using System.Diagnostics.CodeAnalysis; + +using Android.Views; +using Android.Widget; + +using Avalonia.Controls.Platform; +using Avalonia.Platform; + +namespace Avalonia.Android.Platform +{ + internal class AndroidNativeControlHostImpl : INativeControlHostImpl + { + private readonly AvaloniaView _avaloniaView; + + public AndroidNativeControlHostImpl(AvaloniaView avaloniaView) + { + _avaloniaView = avaloniaView; + } + + public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) + { + return new AndroidViewControlHandle(new FrameLayout(_avaloniaView.Context!)); + } + + public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func create) + { + var parent = new AndroidViewControlHandle(_avaloniaView); + AndroidNativeControlAttachment? attachment = null; + try + { + var child = create(parent); + // It has to be assigned to the variable before property setter is called so we dispose it on exception +#pragma warning disable IDE0017 // Simplify object initialization + attachment = new AndroidNativeControlAttachment(child); +#pragma warning restore IDE0017 // Simplify object initialization + attachment.AttachedTo = this; + return attachment; + } + catch + { + attachment?.Dispose(); + throw; + } + } + + public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) + { + return new AndroidNativeControlAttachment(handle) + { + AttachedTo = this + }; + } + + public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == AndroidViewControlHandle.AndroidDescriptor; + + private class AndroidNativeControlAttachment : INativeControlHostControlTopLevelAttachment + { + private View? _view; + private AndroidNativeControlHostImpl? _attachedTo; + + public AndroidNativeControlAttachment(IPlatformHandle child) + { + _view = (child as AndroidViewControlHandle)?.View + ?? Java.Lang.Object.GetObject(child.Handle, global::Android.Runtime.JniHandleOwnership.DoNotTransfer); + } + + [MemberNotNull(nameof(_view))] + private void CheckDisposed() + { + if (_view == null) + throw new ObjectDisposedException(nameof(AndroidNativeControlAttachment)); + } + + public void Dispose() + { + if (_view != null && _attachedTo?._avaloniaView is ViewGroup parent) + { + parent.RemoveView(_view); + } + _attachedTo = null; + _view?.Dispose(); + _view = null; + } + + public INativeControlHostImpl? AttachedTo + { + get => _attachedTo; + set + { + CheckDisposed(); + + var oldAttachedTo = _attachedTo; + _attachedTo = (AndroidNativeControlHostImpl?)value; + if (_attachedTo == null) + { + oldAttachedTo?._avaloniaView.RemoveView(_view); + } + else + { + _attachedTo._avaloniaView.AddView(_view); + } + } + } + + public bool IsCompatibleWith(INativeControlHostImpl host) => host is AndroidNativeControlHostImpl; + + public void HideWithSize(Size size) + { + CheckDisposed(); + if (_attachedTo == null) + return; + + size *= _attachedTo._avaloniaView.TopLevelImpl.RenderScaling; + _view.Visibility = ViewStates.Gone; + _view.LayoutParameters = new FrameLayout.LayoutParams(Math.Max(1, (int)size.Width), Math.Max(1, (int)size.Height)); + _view.RequestLayout(); + } + + public void ShowInBounds(Rect bounds) + { + CheckDisposed(); + if (_attachedTo == null) + throw new InvalidOperationException("The control isn't currently attached to a toplevel"); + + bounds *= _attachedTo._avaloniaView.TopLevelImpl.RenderScaling; + _view.Visibility = ViewStates.Visible; + _view.LayoutParameters = new FrameLayout.LayoutParams(Math.Max(1, (int)bounds.Width), Math.Max(1, (int)bounds.Height)) + { + LeftMargin = (int)bounds.X, + TopMargin = (int)bounds.Y + }; + _view.RequestLayout(); + } + } + } +} diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 8a475676a5..10f98609bf 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -20,7 +20,7 @@ using Avalonia.Rendering; namespace Avalonia.Android.Platform.SkiaPlatform { - class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo, ITopLevelImplWithTextInputMethod + class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo, ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost { private readonly IGlPlatformSurface _gl; private readonly IFramebufferPlatformSurface _framebuffer; @@ -30,9 +30,9 @@ namespace Avalonia.Android.Platform.SkiaPlatform private readonly ITextInputMethodImpl _textInputMethod; private ViewImpl _view; - public TopLevelImpl(Context context, bool placeOnTop = false) + public TopLevelImpl(AvaloniaView avaloniaView, bool placeOnTop = false) { - _view = new ViewImpl(context, this, placeOnTop); + _view = new ViewImpl(avaloniaView.Context, this, placeOnTop); _textInputMethod = new AndroidInputMethod(_view); _keyboardHelper = new AndroidKeyboardEventsHelper(this); _touchHelper = new AndroidTouchEventsHelper(this, () => InputRoot, @@ -40,10 +40,12 @@ namespace Avalonia.Android.Platform.SkiaPlatform _gl = GlPlatformSurface.TryCreate(this); _framebuffer = new FramebufferManager(this); - RenderScaling = (int)_view.Scaling; + RenderScaling = _view.Scaling; MaxClientSize = new PixelSize(_view.Resources.DisplayMetrics.WidthPixels, _view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling); + + NativeControlHost = new AndroidNativeControlHostImpl(avaloniaView); } public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) => @@ -222,6 +224,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform public ITextInputMethodImpl TextInputMethod => _textInputMethod; + public INativeControlHostImpl NativeControlHost { get; } + public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel) { throw new NotImplementedException(); diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs index 2b2a9dd2b4..4cae700c0a 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) + string UnicodeTextInput(KeyEvent keyEvent) { return keyEvent.Action == KeyEventActions.Multiple && keyEvent.RepeatCount == 0 diff --git a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs index 5e97635c9a..4727ea1bfb 100644 --- a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs @@ -31,6 +31,7 @@ namespace Avalonia.Animation.Animators InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, + s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), @@ -41,6 +42,7 @@ namespace Avalonia.Animation.Animators InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, + s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); @@ -50,6 +52,7 @@ namespace Avalonia.Animation.Animators InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, + s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); @@ -102,18 +105,21 @@ namespace Avalonia.Animation.Animators return new ImmutableRadialGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldRadial.GradientStops), solidColorBrush.Opacity, oldRadial.Transform is { } ? new ImmutableTransform(oldRadial.Transform.Value) : null, + oldRadial.TransformOrigin, oldRadial.SpreadMethod, oldRadial.Center, oldRadial.GradientOrigin, oldRadial.Radius); case IConicGradientBrush oldConic: return new ImmutableConicGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldConic.GradientStops), solidColorBrush.Opacity, oldConic.Transform is { } ? new ImmutableTransform(oldConic.Transform.Value) : null, + oldConic.TransformOrigin, oldConic.SpreadMethod, oldConic.Center, oldConic.Angle); case ILinearGradientBrush oldLinear: return new ImmutableLinearGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldLinear.GradientStops), solidColorBrush.Opacity, oldLinear.Transform is { } ? new ImmutableTransform(oldLinear.Transform.Value) : null, + oldLinear.TransformOrigin, oldLinear.SpreadMethod, oldLinear.StartPoint, oldLinear.EndPoint); default: diff --git a/src/Avalonia.Base/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Base/Animation/Animators/TransformAnimator.cs index 34ec8ac503..e12ca722f9 100644 --- a/src/Avalonia.Base/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Base/Animation/Animators/TransformAnimator.cs @@ -43,6 +43,7 @@ namespace Avalonia.Animation.Animators normalTransform.Children.Add(new SkewTransform()); normalTransform.Children.Add(new RotateTransform()); normalTransform.Children.Add(new TranslateTransform()); + normalTransform.Children.Add(new Rotate3DTransform()); ctrl.RenderTransform = normalTransform; } diff --git a/src/Avalonia.Base/Animation/IAnimation.cs b/src/Avalonia.Base/Animation/IAnimation.cs index 436a765d27..eda86ed106 100644 --- a/src/Avalonia.Base/Animation/IAnimation.cs +++ b/src/Avalonia.Base/Animation/IAnimation.cs @@ -1,12 +1,14 @@ using System; using System.Threading; using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Animation { /// /// Interface for Animation objects /// + [NotClientImplementable] public interface IAnimation { /// diff --git a/src/Avalonia.Base/Animation/IAnimationSetter.cs b/src/Avalonia.Base/Animation/IAnimationSetter.cs index 6a1d3539e2..8c4ba95517 100644 --- a/src/Avalonia.Base/Animation/IAnimationSetter.cs +++ b/src/Avalonia.Base/Animation/IAnimationSetter.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Animation { + [NotClientImplementable] public interface IAnimationSetter { AvaloniaProperty? Property { get; set; } diff --git a/src/Avalonia.Base/Animation/IAnimator.cs b/src/Avalonia.Base/Animation/IAnimator.cs index f64ac9f913..06ba8dc329 100644 --- a/src/Avalonia.Base/Animation/IAnimator.cs +++ b/src/Avalonia.Base/Animation/IAnimator.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using Avalonia.Metadata; namespace Avalonia.Animation { /// /// Interface for Animator objects /// + [NotClientImplementable] public interface IAnimator : IList { /// diff --git a/src/Avalonia.Base/Animation/IClock.cs b/src/Avalonia.Base/Animation/IClock.cs index ae44102077..7b3b7b3924 100644 --- a/src/Avalonia.Base/Animation/IClock.cs +++ b/src/Avalonia.Base/Animation/IClock.cs @@ -1,9 +1,9 @@ using System; -using System.Collections.Generic; -using System.Text; +using Avalonia.Metadata; namespace Avalonia.Animation { + [NotClientImplementable] public interface IClock : IObservable { PlayState PlayState { get; set; } diff --git a/src/Avalonia.Base/Animation/IGlobalClock.cs b/src/Avalonia.Base/Animation/IGlobalClock.cs index b0455e2c80..138dc07539 100644 --- a/src/Avalonia.Base/Animation/IGlobalClock.cs +++ b/src/Avalonia.Base/Animation/IGlobalClock.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; +using Avalonia.Metadata; namespace Avalonia.Animation { + [NotClientImplementable] public interface IGlobalClock : IClock { } diff --git a/src/Avalonia.Base/Animation/ITransition.cs b/src/Avalonia.Base/Animation/ITransition.cs index 241ca208d1..639b92ce35 100644 --- a/src/Avalonia.Base/Animation/ITransition.cs +++ b/src/Avalonia.Base/Animation/ITransition.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Animation { /// /// Interface for Transition objects. /// + [NotClientImplementable] public interface ITransition { /// diff --git a/src/Avalonia.Base/Animation/PageSlide.cs b/src/Avalonia.Base/Animation/PageSlide.cs index 6f5d12d824..b22bc8b243 100644 --- a/src/Avalonia.Base/Animation/PageSlide.cs +++ b/src/Avalonia.Base/Animation/PageSlide.cs @@ -10,7 +10,7 @@ using Avalonia.VisualTree; namespace Avalonia.Animation { /// - /// Transitions between two pages by sliding them horizontally. + /// Transitions between two pages by sliding them horizontally or vertically. /// public class PageSlide : IPageTransition { @@ -62,7 +62,7 @@ namespace Avalonia.Animation public Easing SlideOutEasing { get; set; } = new LinearEasing(); /// - public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) + public virtual async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -157,7 +157,7 @@ namespace Avalonia.Animation /// /// Any one of the parameters may be null, but not both. /// - private static IVisual GetVisualParent(IVisual? from, IVisual? to) + protected static IVisual GetVisualParent(IVisual? from, IVisual? to) { var p1 = (from ?? to)!.VisualParent; var p2 = (to ?? from)!.VisualParent; diff --git a/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs new file mode 100644 index 0000000000..239f3aea08 --- /dev/null +++ b/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs @@ -0,0 +1,121 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Media; +using Avalonia.Styling; + +namespace Avalonia.Animation; + +public class Rotate3DTransition: PageSlide +{ + + /// + /// Creates a new instance of the + /// + /// How long the rotation should take place + /// The orientation of the rotation + /// Defines the depth of the 3D Effect. If null, depth will be calculated automatically from the width or height of the common parent of the visual being rotated + public Rotate3DTransition(TimeSpan duration, SlideAxis orientation = SlideAxis.Horizontal, double? depth = null) + : base(duration, orientation) + { + Depth = depth; + } + + /// + /// Defines the depth of the 3D Effect. If null, depth will be calculated automatically from the width or height + /// of the common parent of the visual being rotated. + /// + public double? Depth { get; set; } + + /// + /// Creates a new instance of the + /// + public Rotate3DTransition() { } + + /// + public override async Task Start(Visual? @from, Visual? to, bool forward, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + var tasks = new Task[from != null && to != null ? 2 : 1]; + var parent = GetVisualParent(from, to); + var (rotateProperty, center) = Orientation switch + { + SlideAxis.Vertical => (Rotate3DTransform.AngleXProperty, parent.Bounds.Height), + SlideAxis.Horizontal => (Rotate3DTransform.AngleYProperty, parent.Bounds.Width), + _ => throw new ArgumentOutOfRangeException() + }; + + var depthSetter = new Setter {Property = Rotate3DTransform.DepthProperty, Value = Depth ?? center}; + var centerZSetter = new Setter {Property = Rotate3DTransform.CenterZProperty, Value = -center / 2}; + + KeyFrame CreateKeyFrame(double cue, double rotation, int zIndex, bool isVisible = true) => + new() { + Setters = + { + new Setter { Property = rotateProperty, Value = rotation }, + new Setter { Property = Visual.ZIndexProperty, Value = zIndex }, + new Setter { Property = Visual.IsVisibleProperty, Value = isVisible }, + centerZSetter, + depthSetter + }, + Cue = new Cue(cue) + }; + + if (from != null) + { + var animation = new Animation + { + Easing = SlideOutEasing, + Duration = Duration, + FillMode = FillMode.Forward, + Children = + { + CreateKeyFrame(0d, 0d, 2), + CreateKeyFrame(0.5d, 45d * (forward ? -1 : 1), 1), + CreateKeyFrame(1d, 90d * (forward ? -1 : 1), 1, isVisible: false) + } + }; + + tasks[0] = animation.RunAsync(from, null, cancellationToken); + } + + if (to != null) + { + to.IsVisible = true; + var animation = new Animation + { + Easing = SlideInEasing, + Duration = Duration, + FillMode = FillMode.Forward, + Children = + { + CreateKeyFrame(0d, 90d * (forward ? 1 : -1), 1), + CreateKeyFrame(0.5d, 45d * (forward ? 1 : -1), 1), + CreateKeyFrame(1d, 0d, 2) + } + }; + + tasks[from != null ? 1 : 0] = animation.RunAsync(to, null, cancellationToken); + } + + await Task.WhenAll(tasks); + + if (!cancellationToken.IsCancellationRequested) + { + if (to != null) + { + to.ZIndex = 2; + } + + if (from != null) + { + from.IsVisible = false; + from.ZIndex = 1; + } + } + } +} diff --git a/src/Avalonia.Base/Assets/BiDi.trie b/src/Avalonia.Base/Assets/BiDi.trie deleted file mode 100644 index 1c6122e2f1..0000000000 Binary files a/src/Avalonia.Base/Assets/BiDi.trie and /dev/null differ diff --git a/src/Avalonia.Base/Assets/GraphemeBreak.trie b/src/Avalonia.Base/Assets/GraphemeBreak.trie deleted file mode 100644 index 482bf9b44d..0000000000 Binary files a/src/Avalonia.Base/Assets/GraphemeBreak.trie and /dev/null differ diff --git a/src/Avalonia.Base/Assets/UnicodeData.trie b/src/Avalonia.Base/Assets/UnicodeData.trie deleted file mode 100644 index 46175ea644..0000000000 Binary files a/src/Avalonia.Base/Assets/UnicodeData.trie and /dev/null differ diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 8e4755b4b7..a07e0e3667 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -17,4 +17,21 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 1f14ddede4..6633eabb5d 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -935,7 +935,8 @@ namespace Avalonia public void Dispose() { - _subscription.Dispose(); + // _subscription can be null, if Subscribe failed with an exception. + _subscription?.Dispose(); _owner._directBindings!.Remove(this); } diff --git a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs index 445f35aad2..45c67b9f48 100644 --- a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs +++ b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs @@ -55,7 +55,7 @@ namespace Avalonia /// /// /// This will usually be true, except in - /// + /// /// which receives notifications for all changes to property values, whether a value with a higher /// priority is present or not. When this property is false, the change that is being signaled /// has not resulted in a change to the property value on the object. diff --git a/src/Avalonia.Base/Collections/Pooled/PooledList.cs b/src/Avalonia.Base/Collections/Pooled/PooledList.cs index 200a52fb0d..803b8d60dc 100644 --- a/src/Avalonia.Base/Collections/Pooled/PooledList.cs +++ b/src/Avalonia.Base/Collections/Pooled/PooledList.cs @@ -587,7 +587,7 @@ namespace Avalonia.Collections.Pooled if (size > 0 && _clearOnFree) { // Clear the elements so that the gc can reclaim the references. - Array.Clear(_items, 0, _size); + Array.Clear(_items, 0, size); } } diff --git a/src/Avalonia.Base/Controls/INameScope.cs b/src/Avalonia.Base/Controls/INameScope.cs index 1ca7db2f37..a12e88c055 100644 --- a/src/Avalonia.Base/Controls/INameScope.cs +++ b/src/Avalonia.Base/Controls/INameScope.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using Avalonia.Metadata; using Avalonia.Utilities; namespace Avalonia.Controls @@ -7,6 +6,7 @@ namespace Avalonia.Controls /// /// Defines a name scope. /// + [NotClientImplementable] public interface INameScope { /// diff --git a/src/Avalonia.Base/Controls/IPseudoClasses.cs b/src/Avalonia.Base/Controls/IPseudoClasses.cs index 27290716d0..eda521727f 100644 --- a/src/Avalonia.Base/Controls/IPseudoClasses.cs +++ b/src/Avalonia.Base/Controls/IPseudoClasses.cs @@ -1,9 +1,11 @@ +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Exposes an interface for setting pseudoclasses on a collection. /// + [NotClientImplementable] public interface IPseudoClasses { /// diff --git a/src/Avalonia.Base/Controls/IResourceHost.cs b/src/Avalonia.Base/Controls/IResourceHost.cs index ea34a8b39a..286f0e36ef 100644 --- a/src/Avalonia.Base/Controls/IResourceHost.cs +++ b/src/Avalonia.Base/Controls/IResourceHost.cs @@ -1,6 +1,5 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -10,6 +9,7 @@ namespace Avalonia.Controls /// /// This interface is implemented by and `Application`. /// + [NotClientImplementable] public interface IResourceHost : IResourceNode { /// diff --git a/src/Avalonia.Base/Controls/IResourceNode.cs b/src/Avalonia.Base/Controls/IResourceNode.cs index 73bfeaf161..d6c900f97f 100644 --- a/src/Avalonia.Base/Controls/IResourceNode.cs +++ b/src/Avalonia.Base/Controls/IResourceNode.cs @@ -1,6 +1,5 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -12,6 +11,7 @@ namespace Avalonia.Controls /// () and resource providers such as /// (see ). /// + [NotClientImplementable] public interface IResourceNode { /// diff --git a/src/Avalonia.Base/Controls/ISetInheritanceParent.cs b/src/Avalonia.Base/Controls/ISetInheritanceParent.cs index dbf8c68892..e85e025005 100644 --- a/src/Avalonia.Base/Controls/ISetInheritanceParent.cs +++ b/src/Avalonia.Base/Controls/ISetInheritanceParent.cs @@ -1,4 +1,4 @@ -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -10,6 +10,7 @@ namespace Avalonia.Controls /// Additionally, also sets the inheritance parent; this /// interface is only needed where the logical and inheritance parents differ. /// + [NotClientImplementable] public interface ISetInheritanceParent { /// diff --git a/src/Avalonia.Base/Controls/ISetLogicalParent.cs b/src/Avalonia.Base/Controls/ISetLogicalParent.cs index 85bda05961..7f1b4b5d87 100644 --- a/src/Avalonia.Base/Controls/ISetLogicalParent.cs +++ b/src/Avalonia.Base/Controls/ISetLogicalParent.cs @@ -1,6 +1,5 @@ using Avalonia.LogicalTree; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -10,6 +9,7 @@ namespace Avalonia.Controls /// /// You should not usually need to use this interface - it is for advanced scenarios only. /// + [NotClientImplementable] public interface ISetLogicalParent { /// diff --git a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs index 11d50afe93..c4f4362537 100644 --- a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs @@ -33,7 +33,14 @@ namespace Avalonia.Data.Converters if (typeof(ICommand).IsAssignableFrom(targetType) && value is Delegate d && d.Method.GetParameters().Length <= 1) { - return new MethodToCommandConverter(d); + if (d.Method.IsPrivate == false) + { + return new MethodToCommandConverter(d); + } + else + { + return new BindingNotification(new InvalidCastException("You can't bind to private methods!"), BindingErrorType.Error); + } } if (TypeUtilities.TryConvert(targetType, value, culture, out var result)) diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs index 54785f18e8..4f755ff140 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNode.cs @@ -98,36 +98,36 @@ namespace Avalonia.Data.Core private void ValueChanged(object? value, bool notify) { - if (_subscriber is null) - return; - - var notification = value as BindingNotification; - - if (notification == null) + if (_subscriber is { } subscriber) { - LastValue = value != null ? new WeakReference(value) : NullReference; + var notification = value as BindingNotification; + var next = Next; - if (Next != null) + if (notification == null) { - Next.Target = LastValue; + LastValue = value != null ? new WeakReference(value) : NullReference; + if (next != null) + { + next.Target = LastValue; + } + else if (notify) + { + subscriber(value); + } } - else if (notify) + else { - _subscriber(value); - } - } - else - { - LastValue = notification.Value != null ? new WeakReference(notification.Value) : NullReference; + LastValue = notification.Value != null ? new WeakReference(notification.Value) : NullReference; - if (Next != null) - { - Next.Target = LastValue; - } + if (next != null) + { + next.Target = LastValue; + } - if (Next == null || notification.Error != null) - { - _subscriber(value); + if (next == null || notification.Error != null) + { + subscriber(value); + } } } } diff --git a/src/Avalonia.Base/Data/Core/IPropertyInfo.cs b/src/Avalonia.Base/Data/Core/IPropertyInfo.cs index 4d80feb4ba..0cb8a937cc 100644 --- a/src/Avalonia.Base/Data/Core/IPropertyInfo.cs +++ b/src/Avalonia.Base/Data/Core/IPropertyInfo.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Data.Core { + [NotClientImplementable] public interface IPropertyInfo { string Name { get; } diff --git a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs index 3c4120ad0b..cc6d92ceb7 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.ExceptionServices; +using Avalonia.Utilities; namespace Avalonia.Data.Core.Plugins { @@ -60,11 +61,10 @@ namespace Avalonia.Data.Core.Plugins return AvaloniaPropertyRegistry.Instance.FindRegistered(o, propertyName); } - private class Accessor : PropertyAccessorBase, IObserver + private class Accessor : PropertyAccessorBase, IWeakEventSubscriber { private readonly WeakReference _reference; private readonly AvaloniaProperty _property; - private IDisposable? _subscription; public Accessor(WeakReference reference, AvaloniaProperty property) { @@ -95,29 +95,45 @@ namespace Avalonia.Data.Core.Plugins return false; } - protected override void SubscribeCore() + void IWeakEventSubscriber. + OnEvent(object? notifyPropertyChanged, WeakEvent ev, AvaloniaPropertyChangedEventArgs e) { - _subscription = Instance?.GetObservable(_property).Subscribe(this); + if (e.Property == _property) + { + SendCurrentValue(); + } } - protected override void UnsubscribeCore() + protected override void SubscribeCore() { - _subscription?.Dispose(); - _subscription = null; + SubscribeToChanges(); + SendCurrentValue(); } - void IObserver.OnCompleted() + protected override void UnsubscribeCore() { + var instance = Instance; + + if (instance != null) + WeakEvents.AvaloniaPropertyChanged.Unsubscribe(instance, this); } - void IObserver.OnError(Exception error) + private void SendCurrentValue() { - ExceptionDispatchInfo.Capture(error).Throw(); + try + { + var value = Value; + PublishValue(value); + } + catch { } } - void IObserver.OnNext(object? value) + private void SubscribeToChanges() { - PublishValue(value); + var instance = Instance; + + if (instance != null) + WeakEvents.AvaloniaPropertyChanged.Subscribe(instance, this); } } } diff --git a/src/Avalonia.Base/Data/IBinding.cs b/src/Avalonia.Base/Data/IBinding.cs index 9535cf608e..7d44bf09b5 100644 --- a/src/Avalonia.Base/Data/IBinding.cs +++ b/src/Avalonia.Base/Data/IBinding.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Data { /// /// Holds a binding that can be applied to a property on an object. /// + [NotClientImplementable] public interface IBinding { /// diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index 9c1ffce24c..efcb7dfecb 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -2,7 +2,6 @@ using Avalonia.Data; using Avalonia.Reactive; using Avalonia.Styling; -using Avalonia.Utilities; namespace Avalonia { @@ -188,10 +187,10 @@ namespace Avalonia } else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) { - return new PropertySetterLazyInstance( + return new PropertySetterTemplateInstance( target, this, - () => (TValue)template.Build()); + template); } else { diff --git a/src/Avalonia.Base/GeometryCollection.cs b/src/Avalonia.Base/GeometryCollection.cs deleted file mode 100644 index 0bd02d5438..0000000000 --- a/src/Avalonia.Base/GeometryCollection.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using Avalonia.Animation; - -#nullable enable - -namespace Avalonia.Media -{ - public class GeometryCollection : Animatable, IList, IReadOnlyList - { - private List _inner; - - public GeometryCollection() => _inner = new List(); - public GeometryCollection(IEnumerable collection) => _inner = new List(collection); - public GeometryCollection(int capacity) => _inner = new List(capacity); - - public Geometry this[int index] - { - get => _inner[index]; - set => _inner[index] = value; - } - - public int Count => _inner.Count; - public bool IsReadOnly => false; - - public void Add(Geometry item) => _inner.Add(item); - public void Clear() => _inner.Clear(); - public bool Contains(Geometry item) => _inner.Contains(item); - public void CopyTo(Geometry[] array, int arrayIndex) => _inner.CopyTo(array, arrayIndex); - public IEnumerator GetEnumerator() => _inner.GetEnumerator(); - public int IndexOf(Geometry item) => _inner.IndexOf(item); - public void Insert(int index, Geometry item) => _inner.Insert(index, item); - public bool Remove(Geometry item) => _inner.Remove(item); - public void RemoveAt(int index) => _inner.RemoveAt(index); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} diff --git a/src/Avalonia.Base/IAvaloniaObject.cs b/src/Avalonia.Base/IAvaloniaObject.cs index 00f5062f9e..3b0016903b 100644 --- a/src/Avalonia.Base/IAvaloniaObject.cs +++ b/src/Avalonia.Base/IAvaloniaObject.cs @@ -1,11 +1,13 @@ using System; using Avalonia.Data; +using Avalonia.Metadata; namespace Avalonia { /// /// Interface for getting/setting values on an object. /// + [NotClientImplementable] public interface IAvaloniaObject { /// diff --git a/src/Avalonia.Base/IDataContextProvider.cs b/src/Avalonia.Base/IDataContextProvider.cs index 1172adcaa4..1d381cf4a5 100644 --- a/src/Avalonia.Base/IDataContextProvider.cs +++ b/src/Avalonia.Base/IDataContextProvider.cs @@ -1,10 +1,11 @@ -#nullable enable +using Avalonia.Metadata; namespace Avalonia { /// /// Defines an element with a data context that can be used for binding. /// + [NotClientImplementable] public interface IDataContextProvider : IAvaloniaObject { /// diff --git a/src/Avalonia.Base/IDirectPropertyAccessor.cs b/src/Avalonia.Base/IDirectPropertyAccessor.cs index e34483fa7b..476a36f372 100644 --- a/src/Avalonia.Base/IDirectPropertyAccessor.cs +++ b/src/Avalonia.Base/IDirectPropertyAccessor.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; namespace Avalonia { diff --git a/src/Avalonia.Base/IDirectPropertyMetadata.cs b/src/Avalonia.Base/IDirectPropertyMetadata.cs index 4fd4a3a6b7..7d74470a13 100644 --- a/src/Avalonia.Base/IDirectPropertyMetadata.cs +++ b/src/Avalonia.Base/IDirectPropertyMetadata.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia { /// /// Untyped interface to /// + [NotClientImplementable] public interface IDirectPropertyMetadata { /// diff --git a/src/Avalonia.Base/IStyledElement.cs b/src/Avalonia.Base/IStyledElement.cs index a068d4a5bf..4eed54de45 100644 --- a/src/Avalonia.Base/IStyledElement.cs +++ b/src/Avalonia.Base/IStyledElement.cs @@ -2,12 +2,12 @@ using System.ComponentModel; using Avalonia.Controls; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Styling; -#nullable enable - namespace Avalonia { + [NotClientImplementable] public interface IStyledElement : IStyleable, IStyleHost, diff --git a/src/Avalonia.Base/IStyledPropertyMetadata.cs b/src/Avalonia.Base/IStyledPropertyMetadata.cs index 6b29b5f977..89990338e0 100644 --- a/src/Avalonia.Base/IStyledPropertyMetadata.cs +++ b/src/Avalonia.Base/IStyledPropertyMetadata.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia { /// /// Untyped interface to /// + [NotClientImplementable] public interface IStyledPropertyMetadata { /// diff --git a/src/Avalonia.Base/Input/IAccessKeyHandler.cs b/src/Avalonia.Base/Input/IAccessKeyHandler.cs index e484d003c7..93a50968e2 100644 --- a/src/Avalonia.Base/Input/IAccessKeyHandler.cs +++ b/src/Avalonia.Base/Input/IAccessKeyHandler.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Defines the interface for classes that handle access keys for a window. /// + [Unstable] public interface IAccessKeyHandler { /// diff --git a/src/Avalonia.Base/Input/IFocusManager.cs b/src/Avalonia.Base/Input/IFocusManager.cs index 2510479a8e..0c85cad2f7 100644 --- a/src/Avalonia.Base/Input/IFocusManager.cs +++ b/src/Avalonia.Base/Input/IFocusManager.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Manages focus for the application. /// + [NotClientImplementable] public interface IFocusManager { /// diff --git a/src/Avalonia.Base/Input/IInputDevice.cs b/src/Avalonia.Base/Input/IInputDevice.cs index ab0fae65df..fe4f584f18 100644 --- a/src/Avalonia.Base/Input/IInputDevice.cs +++ b/src/Avalonia.Base/Input/IInputDevice.cs @@ -1,7 +1,9 @@ using Avalonia.Input.Raw; +using Avalonia.Metadata; namespace Avalonia.Input { + [NotClientImplementable] public interface IInputDevice { /// diff --git a/src/Avalonia.Base/Input/IInputElement.cs b/src/Avalonia.Base/Input/IInputElement.cs index d1552a3a2a..43ac87d008 100644 --- a/src/Avalonia.Base/Input/IInputElement.cs +++ b/src/Avalonia.Base/Input/IInputElement.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Interactivity; +using Avalonia.Metadata; using Avalonia.VisualTree; #nullable enable @@ -10,6 +11,7 @@ namespace Avalonia.Input /// /// Defines input-related functionality for a control. /// + [NotClientImplementable] public interface IInputElement : IInteractive, IVisual { /// @@ -40,12 +42,12 @@ namespace Avalonia.Input /// /// Occurs when the pointer enters the control. /// - event EventHandler? PointerEnter; + event EventHandler? PointerEntered; /// /// Occurs when the pointer leaves the control. /// - event EventHandler? PointerLeave; + event EventHandler? PointerExited; /// /// Occurs when the pointer is pressed over the control. diff --git a/src/Avalonia.Base/Input/IInputManager.cs b/src/Avalonia.Base/Input/IInputManager.cs index 80b71d3e47..658226a519 100644 --- a/src/Avalonia.Base/Input/IInputManager.cs +++ b/src/Avalonia.Base/Input/IInputManager.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Input.Raw; +using Avalonia.Metadata; namespace Avalonia.Input { @@ -7,6 +8,7 @@ namespace Avalonia.Input /// Receives input from the windowing subsystem and dispatches it to interested parties /// for processing. /// + [NotClientImplementable] public interface IInputManager { /// diff --git a/src/Avalonia.Base/Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs index 98e8699573..7edc69df52 100644 --- a/src/Avalonia.Base/Input/IInputRoot.cs +++ b/src/Avalonia.Base/Input/IInputRoot.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Defines the interface for top-level input elements. /// + [NotClientImplementable] public interface IInputRoot : IInputElement { /// diff --git a/src/Avalonia.Base/Input/IKeyboardDevice.cs b/src/Avalonia.Base/Input/IKeyboardDevice.cs index d0e84e5ad0..c8db8bf16f 100644 --- a/src/Avalonia.Base/Input/IKeyboardDevice.cs +++ b/src/Avalonia.Base/Input/IKeyboardDevice.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using Avalonia.Metadata; namespace Avalonia.Input { @@ -50,6 +51,7 @@ namespace Avalonia.Input KeyboardMask = Alt | Control | Shift | Meta } + [NotClientImplementable] public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged { IInputElement? FocusedElement { get; } diff --git a/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs index 88d00b3b50..3bcd6d6206 100644 --- a/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs +++ b/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Defines the interface for classes that handle keyboard navigation for a window. /// + [Unstable] public interface IKeyboardNavigationHandler { /// diff --git a/src/Avalonia.Base/Input/IMainMenu.cs b/src/Avalonia.Base/Input/IMainMenu.cs index 67b58c0ffc..213a979c28 100644 --- a/src/Avalonia.Base/Input/IMainMenu.cs +++ b/src/Avalonia.Base/Input/IMainMenu.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Interactivity; +using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Input @@ -7,6 +8,7 @@ namespace Avalonia.Input /// /// Defines the interface for a window's main menu. /// + [NotClientImplementable] public interface IMainMenu : IVisual { /// diff --git a/src/Avalonia.Base/Input/IMouseDevice.cs b/src/Avalonia.Base/Input/IMouseDevice.cs index 6b7f0e76e5..2d66397d63 100644 --- a/src/Avalonia.Base/Input/IMouseDevice.cs +++ b/src/Avalonia.Base/Input/IMouseDevice.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Input { /// /// Represents a mouse device. /// + [NotClientImplementable] public interface IMouseDevice : IPointerDevice { /// diff --git a/src/Avalonia.Base/Input/IPointer.cs b/src/Avalonia.Base/Input/IPointer.cs index 7af48cef82..66aeacadc9 100644 --- a/src/Avalonia.Base/Input/IPointer.cs +++ b/src/Avalonia.Base/Input/IPointer.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { + [NotClientImplementable] public interface IPointer { int Id { get; } diff --git a/src/Avalonia.Base/Input/IPointerDevice.cs b/src/Avalonia.Base/Input/IPointerDevice.cs index 0096bb77bf..0993835feb 100644 --- a/src/Avalonia.Base/Input/IPointerDevice.cs +++ b/src/Avalonia.Base/Input/IPointerDevice.cs @@ -1,9 +1,11 @@ using System; using Avalonia.VisualTree; using Avalonia.Input.Raw; +using Avalonia.Metadata; namespace Avalonia.Input { + [NotClientImplementable] public interface IPointerDevice : IInputDevice { /// diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs index 9fe07f62dd..d0130258c3 100644 --- a/src/Avalonia.Base/Input/InputElement.cs +++ b/src/Avalonia.Base/Input/InputElement.cs @@ -94,7 +94,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent KeyDownEvent = RoutedEvent.Register( - "KeyDown", + nameof(KeyDown), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -102,7 +102,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent KeyUpEvent = RoutedEvent.Register( - "KeyUp", + nameof(KeyUp), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -116,7 +116,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent TextInputEvent = RoutedEvent.Register( - "TextInput", + nameof(TextInput), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -124,27 +124,31 @@ namespace Avalonia.Input /// public static readonly RoutedEvent TextInputMethodClientRequestedEvent = RoutedEvent.Register( - "TextInputMethodClientRequested", + nameof(TextInputMethodClientRequested), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// - /// Defines the event. + /// Defines the event. /// - public static readonly RoutedEvent PointerEnterEvent = - RoutedEvent.Register(nameof(PointerEnter), RoutingStrategies.Direct); + public static readonly RoutedEvent PointerEnteredEvent = + RoutedEvent.Register( + nameof(PointerEntered), + RoutingStrategies.Direct); /// - /// Defines the event. + /// Defines the event. /// - public static readonly RoutedEvent PointerLeaveEvent = - RoutedEvent.Register(nameof(PointerLeave), RoutingStrategies.Direct); + public static readonly RoutedEvent PointerExitedEvent = + RoutedEvent.Register( + nameof(PointerExited), + RoutingStrategies.Direct); /// /// Defines the event. /// public static readonly RoutedEvent PointerMovedEvent = RoutedEvent.Register( - "PointerMove", + nameof(PointerMoved), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -152,7 +156,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent PointerPressedEvent = RoutedEvent.Register( - "PointerPressed", + nameof(PointerPressed), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -160,7 +164,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent PointerReleasedEvent = RoutedEvent.Register( - "PointerReleased", + nameof(PointerReleased), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -168,7 +172,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent PointerCaptureLostEvent = RoutedEvent.Register( - "PointerCaptureLost", + nameof(PointerCaptureLost), RoutingStrategies.Direct); /// @@ -176,7 +180,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent PointerWheelChangedEvent = RoutedEvent.Register( - "PointerWheelChanged", + nameof(PointerWheelChanged), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -208,8 +212,8 @@ namespace Avalonia.Input KeyDownEvent.AddClassHandler((x, e) => x.OnKeyDown(e)); KeyUpEvent.AddClassHandler((x, e) => x.OnKeyUp(e)); TextInputEvent.AddClassHandler((x, e) => x.OnTextInput(e)); - PointerEnterEvent.AddClassHandler((x, e) => x.OnPointerEnterCore(e)); - PointerLeaveEvent.AddClassHandler((x, e) => x.OnPointerLeaveCore(e)); + PointerEnteredEvent.AddClassHandler((x, e) => x.OnPointerEnteredCore(e)); + PointerExitedEvent.AddClassHandler((x, e) => x.OnPointerExitedCore(e)); PointerMovedEvent.AddClassHandler((x, e) => x.OnPointerMoved(e)); PointerPressedEvent.AddClassHandler((x, e) => x.OnPointerPressed(e)); PointerReleasedEvent.AddClassHandler((x, e) => x.OnPointerReleased(e)); @@ -279,19 +283,19 @@ namespace Avalonia.Input /// /// Occurs when the pointer enters the control. /// - public event EventHandler? PointerEnter + public event EventHandler? PointerEntered { - add { AddHandler(PointerEnterEvent, value); } - remove { RemoveHandler(PointerEnterEvent, value); } + add { AddHandler(PointerEnteredEvent, value); } + remove { RemoveHandler(PointerEnteredEvent, value); } } /// /// Occurs when the pointer leaves the control. /// - public event EventHandler? PointerLeave + public event EventHandler? PointerExited { - add { AddHandler(PointerLeaveEvent, value); } - remove { RemoveHandler(PointerLeaveEvent, value); } + add { AddHandler(PointerExitedEvent, value); } + remove { RemoveHandler(PointerExitedEvent, value); } } /// @@ -539,18 +543,18 @@ namespace Avalonia.Input } /// - /// Called before the event occurs. + /// Called before the event occurs. /// /// The event args. - protected virtual void OnPointerEnter(PointerEventArgs e) + protected virtual void OnPointerEntered(PointerEventArgs e) { } /// - /// Called before the event occurs. + /// Called before the event occurs. /// /// The event args. - protected virtual void OnPointerLeave(PointerEventArgs e) + protected virtual void OnPointerExited(PointerEventArgs e) { } @@ -561,7 +565,9 @@ namespace Avalonia.Input protected virtual void OnPointerMoved(PointerEventArgs e) { if (_gestureRecognizers?.HandlePointerMoved(e) == true) + { e.Handled = true; + } } /// @@ -571,7 +577,9 @@ namespace Avalonia.Input protected virtual void OnPointerPressed(PointerPressedEventArgs e) { if (_gestureRecognizers?.HandlePointerPressed(e) == true) + { e.Handled = true; + } } /// @@ -581,7 +589,9 @@ namespace Avalonia.Input protected virtual void OnPointerReleased(PointerReleasedEventArgs e) { if (_gestureRecognizers?.HandlePointerReleased(e) == true) + { e.Handled = true; + } } /// @@ -634,23 +644,23 @@ namespace Avalonia.Input } /// - /// Called before the event occurs. + /// Called before the event occurs. /// /// The event args. - private void OnPointerEnterCore(PointerEventArgs e) + private void OnPointerEnteredCore(PointerEventArgs e) { IsPointerOver = true; - OnPointerEnter(e); + OnPointerEntered(e); } /// - /// Called before the event occurs. + /// Called before the event occurs. /// /// The event args. - private void OnPointerLeaveCore(PointerEventArgs e) + private void OnPointerExitedCore(PointerEventArgs e) { IsPointerOver = false; - OnPointerLeave(e); + OnPointerExited(e); } /// diff --git a/src/Avalonia.Base/Input/Platform/IClipboard.cs b/src/Avalonia.Base/Input/Platform/IClipboard.cs index eb880904eb..bf2a5a8602 100644 --- a/src/Avalonia.Base/Input/Platform/IClipboard.cs +++ b/src/Avalonia.Base/Input/Platform/IClipboard.cs @@ -1,7 +1,9 @@ using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Input.Platform { + [NotClientImplementable] public interface IClipboard { Task GetTextAsync(); diff --git a/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs b/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs index 30d8ee5337..8d1b93087e 100644 --- a/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs +++ b/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs @@ -1,7 +1,9 @@ using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Input.Platform { + [Unstable] public interface IPlatformDragSource { Task DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects); diff --git a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs index d22252893d..67d1eea7e3 100644 --- a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs +++ b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs @@ -97,7 +97,7 @@ namespace Avalonia.Input // Do not pass rootVisual, when we have unknown (negative) position, // so GetPosition won't return invalid values. var hasPosition = position.X >= 0 && position.Y >= 0; - var e = new PointerEventArgs(InputElement.PointerLeaveEvent, element, pointer, + var e = new PointerEventArgs(InputElement.PointerExitedEvent, element, pointer, hasPosition ? root : null, hasPosition ? position : default, timestamp, properties, inputModifiers); @@ -177,7 +177,7 @@ namespace Avalonia.Input el = root.PointerOverElement; - var e = new PointerEventArgs(InputElement.PointerLeaveEvent, el, pointer, root, position, + var e = new PointerEventArgs(InputElement.PointerExitedEvent, el, pointer, root, position, timestamp, properties, inputModifiers); if (el != null && branch != null && !el.IsAttachedToVisualTree) { @@ -195,7 +195,7 @@ namespace Avalonia.Input el = root.PointerOverElement = element; _lastPointer = (pointer, root.PointToScreen(position)); - e.RoutedEvent = InputElement.PointerEnterEvent; + e.RoutedEvent = InputElement.PointerEnteredEvent; while (el != null && el != branch) { diff --git a/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs index f7b7914bd1..3bcc9fadd3 100644 --- a/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs +++ b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs @@ -1,5 +1,8 @@ -namespace Avalonia.Input.Raw +using Avalonia.Metadata; + +namespace Avalonia.Input.Raw { + [NotClientImplementable] public interface IDragDropDevice : IInputDevice { } diff --git a/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs index 4404c903b7..be7ad81f81 100644 --- a/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs +++ b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Input.TextInput { + [Unstable] public interface ITextInputMethodImpl { void SetClient(ITextInputMethodClient? client); @@ -8,6 +11,7 @@ namespace Avalonia.Input.TextInput void Reset(); } + [NotClientImplementable] public interface ITextInputMethodRoot : IInputRoot { ITextInputMethodImpl? InputMethod { get; } diff --git a/src/Avalonia.Base/Interactivity/IInteractive.cs b/src/Avalonia.Base/Interactivity/IInteractive.cs index 6d7dcd64f4..980bf54f1f 100644 --- a/src/Avalonia.Base/Interactivity/IInteractive.cs +++ b/src/Avalonia.Base/Interactivity/IInteractive.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; #nullable enable @@ -7,6 +8,7 @@ namespace Avalonia.Interactivity /// /// Interface for objects that raise routed events. /// + [NotClientImplementable] public interface IInteractive { /// diff --git a/src/Avalonia.Base/Layout/ILayoutManager.cs b/src/Avalonia.Base/Layout/ILayoutManager.cs index 614670a53b..143ce13a1b 100644 --- a/src/Avalonia.Base/Layout/ILayoutManager.cs +++ b/src/Avalonia.Base/Layout/ILayoutManager.cs @@ -1,12 +1,12 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Layout { /// /// Manages measuring and arranging of controls. /// + [NotClientImplementable] public interface ILayoutManager : IDisposable { /// diff --git a/src/Avalonia.Base/Layout/ILayoutRoot.cs b/src/Avalonia.Base/Layout/ILayoutRoot.cs index e2f16b338a..df15fc1a1d 100644 --- a/src/Avalonia.Base/Layout/ILayoutRoot.cs +++ b/src/Avalonia.Base/Layout/ILayoutRoot.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Layout { /// /// Defines the root of a layoutable tree. /// + [NotClientImplementable] public interface ILayoutRoot : ILayoutable { /// diff --git a/src/Avalonia.Base/Layout/ILayoutable.cs b/src/Avalonia.Base/Layout/ILayoutable.cs index 54d3ba6a11..d8b546b04a 100644 --- a/src/Avalonia.Base/Layout/ILayoutable.cs +++ b/src/Avalonia.Base/Layout/ILayoutable.cs @@ -1,12 +1,12 @@ +using Avalonia.Metadata; using Avalonia.VisualTree; -#nullable enable - namespace Avalonia.Layout { /// /// Defines layout-related functionality for a control. /// + [NotClientImplementable] public interface ILayoutable : IVisual { /// diff --git a/src/Avalonia.Base/Layout/LayoutManager.cs b/src/Avalonia.Base/Layout/LayoutManager.cs index fc988a8d6c..446f135c83 100644 --- a/src/Avalonia.Base/Layout/LayoutManager.cs +++ b/src/Avalonia.Base/Layout/LayoutManager.cs @@ -28,7 +28,7 @@ namespace Avalonia.Layout public LayoutManager(ILayoutRoot owner) { _owner = owner ?? throw new ArgumentNullException(nameof(owner)); - _executeLayoutPass = ExecuteLayoutPass; + _executeLayoutPass = ExecuteQueuedLayoutPass; } public virtual event EventHandler? LayoutUpdated; @@ -94,6 +94,16 @@ namespace Avalonia.Layout QueueLayoutPass(); } + private void ExecuteQueuedLayoutPass() + { + if (!_queued) + { + return; + } + + ExecuteLayoutPass(); + } + /// public virtual void ExecuteLayoutPass() { @@ -319,8 +329,8 @@ namespace Avalonia.Layout { if (!_queued && !_running) { - Dispatcher.UIThread.Post(_executeLayoutPass, DispatcherPriority.Layout); _queued = true; + Dispatcher.UIThread.Post(_executeLayoutPass, DispatcherPriority.Layout); } } diff --git a/src/Avalonia.Base/Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs index 0b74d5915a..101e867d56 100644 --- a/src/Avalonia.Base/Layout/Layoutable.cs +++ b/src/Avalonia.Base/Layout/Layoutable.cs @@ -141,7 +141,6 @@ namespace Avalonia.Layout static Layoutable() { AffectsMeasure( - IsVisibleProperty, WidthProperty, HeightProperty, MinWidthProperty, @@ -797,6 +796,25 @@ namespace Avalonia.Layout { } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == IsVisibleProperty) + { + DesiredSize = default; + + // All changes to visibility cause the parent element to be notified. + this.GetVisualParent()?.ChildDesiredSizeChanged(this); + + // We only invalidate outselves when visibility is changed to true. + if (change.GetNewValue()) + { + InvalidateMeasure(); + } + } + } + /// protected sealed override void OnVisualParentChanged(IVisual? oldParent, IVisual? newParent) { diff --git a/src/Avalonia.Base/LogicalTree/ILogical.cs b/src/Avalonia.Base/LogicalTree/ILogical.cs index caff3d8150..9cc2edff86 100644 --- a/src/Avalonia.Base/LogicalTree/ILogical.cs +++ b/src/Avalonia.Base/LogicalTree/ILogical.cs @@ -1,12 +1,14 @@ using System; using Avalonia.Collections; using Avalonia.Controls; +using Avalonia.Metadata; namespace Avalonia.LogicalTree { /// /// Represents a node in the logical tree. /// + [NotClientImplementable] public interface ILogical { /// diff --git a/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs b/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs index 4a61544a6f..ea0f554e96 100644 --- a/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs +++ b/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs @@ -1,8 +1,11 @@ -namespace Avalonia.LogicalTree +using Avalonia.Metadata; + +namespace Avalonia.LogicalTree { /// /// Represents a root of a logical tree. /// + [NotClientImplementable] public interface ILogicalRoot : ILogical { } diff --git a/src/Avalonia.Base/Matrix.cs b/src/Avalonia.Base/Matrix.cs index b08a0eb98a..df6439b1b3 100644 --- a/src/Avalonia.Base/Matrix.cs +++ b/src/Avalonia.Base/Matrix.cs @@ -1,12 +1,22 @@ using System; using System.Globalization; +using System.Linq; +using System.Numerics; using Avalonia.Utilities; namespace Avalonia { /// - /// A 2x3 matrix. + /// A 3x3 matrix. /// + /// Matrix layout: + /// | 1st col | 2nd col | 3r col | + /// 1st row | scaleX | skewY | perspX | + /// 2nd row | skewX | scaleY | perspY | + /// 3rd row | transX | transY | perspZ | + /// + /// Note: Skia.SkMatrix uses a transposed layout (where for example skewX/skewY and persp0/transX are swapped). + /// #if !BUILDTASK public #endif @@ -14,40 +24,76 @@ namespace Avalonia { private readonly double _m11; private readonly double _m12; + private readonly double _m13; private readonly double _m21; private readonly double _m22; + private readonly double _m23; private readonly double _m31; private readonly double _m32; + private readonly double _m33; + + /// + /// Initializes a new instance of the struct (equivalent to a 2x3 Matrix without perspective). + /// + /// The first element of the first row. + /// The second element of the first row. + /// The first element of the second row. + /// The second element of the second row. + /// The first element of the third row. + /// The second element of the third row. + public Matrix( + double scaleX, + double skewY, + double skewX, + double scaleY, + double offsetX, + double offsetY) : this( scaleX, skewY, 0, skewX, scaleY, 0, offsetX, offsetY, 1) + { + } + + /// /// Initializes a new instance of the struct. /// - /// The first element of the first row. - /// The second element of the first row. - /// The first element of the second row. - /// The second element of the second row. + /// The first element of the first row. + /// The second element of the first row. + /// The third element of the first row. + /// The first element of the second row. + /// The second element of the second row. + /// The third element of the second row. /// The first element of the third row. /// The second element of the third row. + /// The third element of the third row. public Matrix( - double m11, - double m12, - double m21, - double m22, + double scaleX, + double skewY, + double perspX, + double skewX, + double scaleY, + double perspY, double offsetX, - double offsetY) + double offsetY, + double perspZ) { - _m11 = m11; - _m12 = m12; - _m21 = m21; - _m22 = m22; + _m11 = scaleX; + _m12 = skewY; + _m13 = perspX; + _m21 = skewX; + _m22 = scaleY; + _m23 = perspY; _m31 = offsetX; _m32 = offsetY; + _m33 = perspZ; } /// /// Returns the multiplicative identity matrix. /// - public static Matrix Identity { get; } = new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + public static Matrix Identity { get; } = new Matrix( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); /// /// Returns whether the matrix is the identity matrix. @@ -60,35 +106,50 @@ namespace Avalonia public bool HasInverse => !MathUtilities.IsZero(GetDeterminant()); /// - /// The first element of the first row + /// The first element of the first row (scaleX). /// public double M11 => _m11; /// - /// The second element of the first row + /// The second element of the first row (skewY). /// public double M12 => _m12; /// - /// The first element of the second row + /// The third element of the first row (perspX: input x-axis perspective factor). + /// + public double M13 => _m13; + + /// + /// The first element of the second row (skewX). /// public double M21 => _m21; /// - /// The second element of the second row + /// The second element of the second row (scaleY). /// public double M22 => _m22; /// - /// The first element of the third row + /// The third element of the second row (perspY: input y-axis perspective factor). + /// + public double M23 => _m23; + + /// + /// The first element of the third row (offsetX/translateX). /// public double M31 => _m31; /// - /// The second element of the third row + /// The second element of the third row (offsetY/translateY). /// public double M32 => _m32; + /// + /// The third element of the third row (perspZ: perspective scale factor). + /// + public double M33 => _m33; + /// /// Multiplies two matrices together and returns the resulting matrix. /// @@ -98,12 +159,15 @@ namespace Avalonia public static Matrix operator *(Matrix value1, Matrix value2) { return new Matrix( - (value1.M11 * value2.M11) + (value1.M12 * value2.M21), - (value1.M11 * value2.M12) + (value1.M12 * value2.M22), - (value1.M21 * value2.M11) + (value1.M22 * value2.M21), - (value1.M21 * value2.M12) + (value1.M22 * value2.M22), - (value1._m31 * value2.M11) + (value1._m32 * value2.M21) + value2._m31, - (value1._m31 * value2.M12) + (value1._m32 * value2.M22) + value2._m32); + (value1.M11 * value2.M11) + (value1.M12 * value2.M21) + (value1.M13 * value2.M31), + (value1.M11 * value2.M12) + (value1.M12 * value2.M22) + (value1.M13 * value2.M32), + (value1.M11 * value2.M13) + (value1.M12 * value2.M23) + (value1.M13 * value2.M33), + (value1.M21 * value2.M11) + (value1.M22 * value2.M21) + (value1.M23 * value2.M31), + (value1.M21 * value2.M12) + (value1.M22 * value2.M22) + (value1.M23 * value2.M32), + (value1.M21 * value2.M13) + (value1.M22 * value2.M23) + (value1.M23 * value2.M33), + (value1.M31 * value2.M11) + (value1.M32 * value2.M21) + (value1.M33 * value2.M31), + (value1.M31 * value2.M12) + (value1.M32 * value2.M22) + (value1.M33 * value2.M32), + (value1.M31 * value2.M13) + (value1.M32 * value2.M23) + (value1.M33 * value2.M33)); } /// @@ -171,7 +235,7 @@ namespace Avalonia /// A scaling matrix. public static Matrix CreateScale(double xScale, double yScale) { - return CreateScale(new Vector(xScale, yScale)); + return new Matrix(xScale, 0, 0, yScale, 0, 0); } /// @@ -181,7 +245,7 @@ namespace Avalonia /// A scaling matrix. public static Matrix CreateScale(Vector scales) { - return new Matrix(scales.X, 0, 0, scales.Y, 0, 0); + return CreateScale(scales.X, scales.Y); } /// @@ -214,7 +278,7 @@ namespace Avalonia { return angle * 0.0174532925; } - + /// /// Appends another matrix as post-multiplication operation. /// Equivalent to this * value; @@ -227,7 +291,7 @@ namespace Avalonia } /// - /// Prpends another matrix as pre-multiplication operation. + /// Prepends another matrix as pre-multiplication operation. /// Equivalent to value * this; /// /// A matrix. @@ -247,7 +311,49 @@ namespace Avalonia /// public double GetDeterminant() { - return (_m11 * _m22) - (_m12 * _m21); + // implemented using "Laplace expansion": + return _m11 * (_m22 * _m33 - _m23 * _m32) + - _m12 * (_m21 * _m33 - _m23 * _m31) + + _m13 * (_m21 * _m32 - _m22 * _m31); + } + + /// + /// Transforms the point with the matrix + /// + /// The point to be transformed + /// The transformed point + public Point Transform(Point p) + { + Point transformedResult; + + // If this matrix contains a non-affine transform with need to extend + // the point to a 3D vector and flatten it back for 2d display + // by multiplying X and Y with the inverse of the Z axis. + // The code below also works with affine transformations, but for performance (and compatibility) + // reasons we will use the more complex calculation only if necessary + if (ContainsPerspective()) + { + var m44 = new Matrix4x4( + (float)M11, (float)M12, (float)M13, 0, + (float)M21, (float)M22, (float)M23, 0, + (float)M31, (float)M32, (float)M33, 0, + 0, 0, 0, 1 + ); + + var vector = new Vector3((float)p.X, (float)p.Y, 1); + var transformedVector = Vector3.Transform(vector, m44); + var z = 1 / transformedVector.Z; + + transformedResult = new Point(transformedVector.X * z, transformedVector.Y * z); + } + else + { + return new Point( + (p.X * M11) + (p.Y * M21) + M31, + (p.X * M12) + (p.Y * M22) + M32); + } + + return transformedResult; } /// @@ -260,10 +366,13 @@ namespace Avalonia // ReSharper disable CompareOfFloatsByEqualityOperator return _m11 == other.M11 && _m12 == other.M12 && + _m13 == other.M13 && _m21 == other.M21 && _m22 == other.M22 && + _m23 == other.M23 && _m31 == other.M31 && - _m32 == other.M32; + _m32 == other.M32 && + _m33 == other.M33; // ReSharper restore CompareOfFloatsByEqualityOperator } @@ -280,9 +389,18 @@ namespace Avalonia /// The hash code. public override int GetHashCode() { - return M11.GetHashCode() + M12.GetHashCode() + - M21.GetHashCode() + M22.GetHashCode() + - M31.GetHashCode() + M32.GetHashCode(); + return (_m11, _m12, _m13, _m21, _m22, _m23, _m31, _m32, _m33).GetHashCode(); + } + + /// + /// Determines if the current matrix contains perspective (non-affine) transforms (true) or only (affine) transforms that could be mapped into an 2x3 matrix (false). + /// + public bool ContainsPerspective() + { + + // ReSharper disable CompareOfFloatsByEqualityOperator + return _m13 != 0 || _m23 != 0 || _m33 != 1; + // ReSharper restore CompareOfFloatsByEqualityOperator } /// @@ -292,15 +410,25 @@ namespace Avalonia public override string ToString() { CultureInfo ci = CultureInfo.CurrentCulture; + + string msg; + double[] values; + + if (ContainsPerspective()) + { + msg = "{{ {{M11:{0} M12:{1} M13:{2}}} {{M21:{3} M22:{4} M23:{5}}} {{M31:{6} M32:{7} M33:{8}}} }}"; + values = new[] { M11, M12, M13, M21, M22, M23, M31, M32, M33 }; + } + else + { + msg = "{{ {{M11:{0} M12:{1}}} {{M21:{2} M22:{3}}} {{M31:{4} M32:{5}}} }}"; + values = new[] { M11, M12, M21, M22, M31, M32 }; + } + return string.Format( ci, - "{{ {{M11:{0} M12:{1}}} {{M21:{2} M22:{3}}} {{M31:{4} M32:{5}}} }}", - M11.ToString(ci), - M12.ToString(ci), - M21.ToString(ci), - M22.ToString(ci), - M31.ToString(ci), - M32.ToString(ci)); + msg, + values.Select((v) => v.ToString(ci)).ToArray()); } /// @@ -318,14 +446,20 @@ namespace Avalonia return false; } + var invdet = 1 / d; + inverted = new Matrix( - _m22 / d, - -_m12 / d, - -_m21 / d, - _m11 / d, - ((_m21 * _m32) - (_m22 * _m31)) / d, - ((_m12 * _m31) - (_m11 * _m32)) / d); - + (_m22 * _m33 - _m32 * _m23) * invdet, + (_m13 * _m32 - _m12 * _m33) * invdet, + (_m12 * _m23 - _m13 * _m22) * invdet, + (_m23 * _m31 - _m21 * _m33) * invdet, + (_m11 * _m33 - _m13 * _m31) * invdet, + (_m21 * _m13 - _m11 * _m23) * invdet, + (_m21 * _m32 - _m31 * _m22) * invdet, + (_m31 * _m12 - _m11 * _m32) * invdet, + (_m11 * _m22 - _m21 * _m12) * invdet + ); + return true; } @@ -336,7 +470,7 @@ namespace Avalonia /// The inverted matrix. public Matrix Invert() { - if (!TryInvert(out Matrix inverted)) + if (!TryInvert(out var inverted)) { throw new InvalidOperationException("Transform is not invertible."); } @@ -347,20 +481,30 @@ namespace Avalonia /// /// Parses a string. /// - /// Six comma-delimited double values (m11, m12, m21, m22, offsetX, offsetY) that describe the new + /// Six or nine comma-delimited double values (m11, m12, m21, m22, offsetX, offsetY[, perspX, perspY, perspZ]) that describe the new /// The . public static Matrix Parse(string s) { + // initialize to satisfy compiler - only used when retrieved from string. + double v8 = 0; + double v9 = 0; + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Matrix.")) { - return new Matrix( - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble() - ); + var v1 = tokenizer.ReadDouble(); + var v2 = tokenizer.ReadDouble(); + var v3 = tokenizer.ReadDouble(); + var v4 = tokenizer.ReadDouble(); + var v5 = tokenizer.ReadDouble(); + var v6 = tokenizer.ReadDouble(); + var persp = tokenizer.TryReadDouble(out var v7); + persp = persp && tokenizer.TryReadDouble(out v8); + persp = persp && tokenizer.TryReadDouble(out v9); + + if (persp) + return new Matrix(v1, v2, v7, v3, v4, v8, v5, v6, v9); + else + return new Matrix(v1, v2, v3, v4, v5, v6); } } @@ -369,14 +513,14 @@ namespace Avalonia /// /// Matrix to decompose. /// Decomposed matrix. - /// The status of the operation. + /// The status of the operation. public static bool TryDecomposeTransform(Matrix matrix, out Decomposed decomposed) { decomposed = default; var determinant = matrix.GetDeterminant(); - if (MathUtilities.IsZero(determinant)) + if (MathUtilities.IsZero(determinant) || matrix.ContainsPerspective()) { return false; } diff --git a/src/Avalonia.Base/Media/Brush.cs b/src/Avalonia.Base/Media/Brush.cs index 9d989979a7..8d531e9394 100644 --- a/src/Avalonia.Base/Media/Brush.cs +++ b/src/Avalonia.Base/Media/Brush.cs @@ -24,6 +24,12 @@ namespace Avalonia.Media public static readonly StyledProperty TransformProperty = AvaloniaProperty.Register(nameof(Transform)); + /// + /// Defines the property + /// + public static readonly StyledProperty TransformOriginProperty = + AvaloniaProperty.Register(nameof(TransformOrigin)); + /// public event EventHandler? Invalidated; @@ -51,6 +57,15 @@ namespace Avalonia.Media set { SetValue(TransformProperty, value); } } + /// + /// Gets or sets the origin of the brush + /// + public RelativePoint TransformOrigin + { + get => GetValue(TransformOriginProperty); + set => SetValue(TransformOriginProperty, value); + } + /// /// Parses a brush string. /// diff --git a/src/Avalonia.Base/Media/DrawingCollection.cs b/src/Avalonia.Base/Media/DrawingCollection.cs new file mode 100644 index 0000000000..a76f7743cc --- /dev/null +++ b/src/Avalonia.Base/Media/DrawingCollection.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Avalonia.Collections; + +namespace Avalonia.Media +{ + public sealed class DrawingCollection : AvaloniaList + { + public DrawingCollection() + { + ResetBehavior = ResetBehavior.Remove; + } + + public DrawingCollection(IEnumerable items) : base(items) + { + ResetBehavior = ResetBehavior.Remove; + } + } +} diff --git a/src/Avalonia.Base/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs index eeb6318ebd..603bb1c1c1 100644 --- a/src/Avalonia.Base/Media/DrawingGroup.cs +++ b/src/Avalonia.Base/Media/DrawingGroup.cs @@ -1,6 +1,10 @@ -using Avalonia.Collections; +using System; +using System.Collections.Generic; +using Avalonia.Media.Imaging; using Avalonia.Metadata; using Avalonia.Platform; +using Avalonia.Rendering.SceneGraph; +using Avalonia.Utilities; namespace Avalonia.Media { @@ -18,6 +22,14 @@ namespace Avalonia.Media public static readonly StyledProperty OpacityMaskProperty = AvaloniaProperty.Register(nameof(OpacityMask)); + public static readonly DirectProperty ChildrenProperty = + AvaloniaProperty.RegisterDirect( + nameof(Children), + o => o.Children, + (o, v) => o.Children = v); + + private DrawingCollection _children = new DrawingCollection(); + public double Opacity { get => GetValue(OpacityProperty); @@ -42,8 +54,23 @@ namespace Avalonia.Media set => SetValue(OpacityMaskProperty, value); } + /// + /// Gets or sets the collection that contains the child geometries. + /// [Content] - public AvaloniaList Children { get; } = new AvaloniaList(); + public DrawingCollection Children + { + get => _children; + set + { + SetAndRaise(ChildrenProperty, ref _children, value); + } + } + + public DrawingContext Open() + { + return new DrawingContext(new DrawingGroupDrawingContext(this)); + } public override void Draw(DrawingContext context) { @@ -75,5 +102,394 @@ namespace Avalonia.Media return rect; } + + private class DrawingGroupDrawingContext : IDrawingContextImpl + { + private readonly DrawingGroup _drawingGroup; + private readonly IPlatformRenderInterface _platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); + + private Matrix _transform; + + private bool _disposed; + + // Root drawing created by this DrawingContext. + // + // If there is only a single child of the root DrawingGroup, _rootDrawing + // will reference the single child, and the root _currentDrawingGroup + // value will be null. Otherwise, _rootDrawing will reference the + // root DrawingGroup, and be the same value as the root _currentDrawingGroup. + // + // Either way, _rootDrawing always references the root drawing. + protected Drawing? _rootDrawing; + + // Current DrawingGroup that new children are added to + protected DrawingGroup? _currentDrawingGroup; + + // Previous values of _currentDrawingGroup + private Stack? _previousDrawingGroupStack; + + public DrawingGroupDrawingContext(DrawingGroup drawingGroup) + { + _drawingGroup = drawingGroup; + } + + public Matrix Transform + { + get => _transform; + set + { + _transform = value; + PushTransform(new MatrixTransform(value)); + } + } + + public void DrawEllipse(IBrush? brush, IPen? pen, Rect rect) + { + if ((brush == null) && (pen == null)) + { + return; + } + + // Instantiate the geometry + var geometry = _platformRenderInterface.CreateEllipseGeometry(rect); + + // Add Drawing to the Drawing graph + AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry)); + } + + public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry) + { + if (((brush == null) && (pen == null)) || (geometry == null)) + { + return; + } + + AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry)); + } + + public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun) + { + if (foreground == null || glyphRun == null) + { + return; + } + + // Add a GlyphRunDrawing to the Drawing graph + GlyphRunDrawing glyphRunDrawing = new GlyphRunDrawing + { + Foreground = foreground, + GlyphRun = glyphRun, + }; + + // Add Drawing to the Drawing graph + AddDrawing(glyphRunDrawing); + } + + public void DrawLine(IPen pen, Point p1, Point p2) + { + if (pen == null) + { + return; + } + + // Instantiate the geometry + var geometry = _platformRenderInterface.CreateLineGeometry(p1, p2); + + // Add Drawing to the Drawing graph + AddNewGeometryDrawing(null, pen, new PlatformGeometry(geometry)); + } + + public void DrawRectangle(IBrush? brush, IPen? pen, RoundedRect rect, BoxShadows boxShadows = default) + { + if ((brush == null) && (pen == null)) + { + return; + } + + // Instantiate the geometry + var geometry = _platformRenderInterface.CreateRectangleGeometry(rect.Rect); + + // Add Drawing to the Drawing graph + AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry)); + } + + public void Clear(Color color) + { + throw new NotImplementedException(); + } + + public IDrawingContextLayerImpl CreateLayer(Size size) + { + throw new NotImplementedException(); + } + + public void Custom(ICustomDrawOperation custom) + { + throw new NotImplementedException(); + } + + public void DrawBitmap(IRef source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) + { + throw new NotImplementedException(); + } + + public void DrawBitmap(IRef source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect) + { + throw new NotImplementedException(); + } + + public void PopBitmapBlendMode() + { + throw new NotImplementedException(); + } + + public void PopClip() + { + throw new NotImplementedException(); + } + + public void PopGeometryClip() + { + throw new NotImplementedException(); + } + + public void PopOpacity() + { + throw new NotImplementedException(); + } + + public void PopOpacityMask() + { + throw new NotImplementedException(); + } + + public void PushBitmapBlendMode(BitmapBlendingMode blendingMode) + { + throw new NotImplementedException(); + } + + public void PushClip(Rect clip) + { + throw new NotImplementedException(); + } + + public void PushClip(RoundedRect clip) + { + throw new NotImplementedException(); + } + + public void PushGeometryClip(IGeometryImpl clip) + { + throw new NotImplementedException(); + } + + public void PushOpacity(double opacity) + { + throw new NotImplementedException(); + } + + public void PushOpacityMask(IBrush mask, Rect bounds) + { + throw new NotImplementedException(); + } + + public void Dispose() + { + // Dispose may be called multiple times without throwing + // an exception. + if (!_disposed) + { + // Match any outstanding Push calls with a Pop + if (_previousDrawingGroupStack != null) + { + int stackCount = _previousDrawingGroupStack.Count; + for (int i = 0; i < stackCount; i++) + { + Pop(); + } + } + + // Call CloseCore with the root DrawingGroup's children + DrawingCollection rootChildren; + + if (_currentDrawingGroup != null) + { + // If we created a root DrawingGroup because multiple elements + // exist at the root level, provide it's Children collection + // directly. + rootChildren = _currentDrawingGroup.Children; + } + else + { + // Create a new DrawingCollection if we didn't create a + // root DrawingGroup because the root level only contained + // a single child. + // + // This collection is needed by DrawingGroup.Open because + // Open always replaces it's Children collection. It isn't + // strictly needed for Append, but always using a collection + // simplifies the TransactionalAppend implementation (i.e., + // a seperate implemention isn't needed for a single element) + rootChildren = new DrawingCollection(); + + // + // We may need to opt-out of inheritance through the new Freezable. + // This is controlled by this.CanBeInheritanceContext. + // + if (_rootDrawing != null) + { + rootChildren.Add(_rootDrawing); + } + } + + // Inform our derived classes that Close was called + _drawingGroup.Children = rootChildren; + + _disposed = true; + } + } + + /// + /// Pop + /// + private void Pop() + { + // Verify that Pop hasn't been called too many times + if ((_previousDrawingGroupStack == null) || (_previousDrawingGroupStack.Count == 0)) + { + throw new InvalidOperationException("DrawingGroupStack count missmatch."); + } + + // Restore the previous value of the current drawing group + _currentDrawingGroup = _previousDrawingGroupStack.Pop(); + } + + /// + /// PushTransform - + /// Push a Transform which will apply to all drawing operations until the corresponding + /// Pop. + /// + /// The Transform to push. + private void PushTransform(Transform transform) + { + // Instantiate a new drawing group and set it as the _currentDrawingGroup + var drawingGroup = PushNewDrawingGroup(); + + // Set the transform on the new DrawingGroup + drawingGroup.Transform = transform; + } + + /// + /// Creates a new DrawingGroup for a Push* call by setting the + /// _currentDrawingGroup to a newly instantiated DrawingGroup, + /// and saving the previous _currentDrawingGroup value on the + /// _previousDrawingGroupStack. + /// + private DrawingGroup PushNewDrawingGroup() + { + // Instantiate a new drawing group + DrawingGroup drawingGroup = new DrawingGroup(); + + // Add it to the drawing graph, like any other Drawing + AddDrawing(drawingGroup); + + // Lazily allocate the stack when it is needed because many uses + // of DrawingDrawingContext will have a depth of one. + if (null == _previousDrawingGroupStack) + { + _previousDrawingGroupStack = new Stack(2); + } + + // Save the previous _currentDrawingGroup value. + // + // If this is the first call, the value of _currentDrawingGroup + // will be null because AddDrawing doesn't create a _currentDrawingGroup + // for the first drawing. Having null on the stack is valid, and simply + // denotes that this new DrawingGroup is the first child in the root + // DrawingGroup. It is also possible for the first value on the stack + // to be non-null, which means that the root DrawingGroup has other + // children. + _previousDrawingGroupStack.Push(_currentDrawingGroup); + + // Set this drawing group as the current one so that subsequent drawing's + // are added as it's children until Pop is called. + _currentDrawingGroup = drawingGroup; + + return drawingGroup; + } + + /// + /// Contains the functionality common to GeometryDrawing operations of + /// instantiating the GeometryDrawing, setting it's Freezable state, + /// and Adding it to the Drawing Graph. + /// + private void AddNewGeometryDrawing(IBrush? brush, IPen? pen, Geometry? geometry) + { + if (geometry == null) + { + throw new ArgumentNullException(nameof(geometry)); + } + + // Instantiate the GeometryDrawing + GeometryDrawing geometryDrawing = new GeometryDrawing + { + // We may need to opt-out of inheritance through the new Freezable. + // This is controlled by this.CanBeInheritanceContext. + Brush = brush, + Pen = pen, + Geometry = geometry + }; + + // Add it to the drawing graph + AddDrawing(geometryDrawing); + } + + /// + /// Adds a new Drawing to the DrawingGraph. + /// + /// This method avoids creating a DrawingGroup for the common case + /// where only a single child exists in the root DrawingGroup. + /// + private void AddDrawing(Drawing newDrawing) + { + if (newDrawing == null) + { + throw new ArgumentNullException(nameof(newDrawing)); + } + + if (_rootDrawing == null) + { + // When a DrawingGroup is set, it should be made the root if + // a root drawing didnt exist. + Contract.Requires(_currentDrawingGroup == null); + + // If this is the first Drawing being added, avoid creating a DrawingGroup + // and set this drawing as the root drawing. This optimizes the common + // case where only a single child exists in the root DrawingGroup. + _rootDrawing = newDrawing; + } + else if (_currentDrawingGroup == null) + { + // When the second drawing is added at the root level, set a + // DrawingGroup as the root and add both drawings to it. + + // Instantiate the DrawingGroup + _currentDrawingGroup = new DrawingGroup(); + + // Add both Children + _currentDrawingGroup.Children.Add(_rootDrawing); + _currentDrawingGroup.Children.Add(newDrawing); + + // Set the new DrawingGroup as the current + _rootDrawing = _currentDrawingGroup; + } + else + { + // If there already is a current drawing group, then simply add + // the new drawing too it. + _currentDrawingGroup.Children.Add(newDrawing); + } + } + } } } diff --git a/src/Avalonia.Base/Media/FormattedText.cs b/src/Avalonia.Base/Media/FormattedText.cs index 7bdf59def0..5480336f84 100644 --- a/src/Avalonia.Base/Media/FormattedText.cs +++ b/src/Avalonia.Base/Media/FormattedText.cs @@ -1223,7 +1223,7 @@ namespace Avalonia.Media public double OverhangTrailing { get - { + { return BlackBoxMetrics.OverhangTrailing; } } @@ -1252,6 +1252,46 @@ namespace Avalonia.Media } } + /// + /// Obtains geometry for the text, including underlines and strikethroughs. + /// + /// The left top origin of the resulting geometry. + /// The geometry returned contains the combined geometry + /// of all of the glyphs, underlines and strikeThroughs that represent the formatted text. + /// Overlapping contours are merged by performing a Boolean union operation. + public Geometry? BuildGeometry(Point origin) + { + GeometryGroup? accumulatedGeometry = null; + var lineOrigin = origin; + + DrawingGroup drawing = new DrawingGroup(); + + using (var ctx = drawing.Open()) + { + using (var enumerator = GetEnumerator()) + { + while (enumerator.MoveNext()) + { + var currentLine = enumerator.Current; + + if (currentLine != null) + { + currentLine.Draw(ctx, lineOrigin); + + AdvanceLineOrigin(ref lineOrigin, currentLine); + } + } + } + } + + Transform? transform = new TranslateTransform(origin.X, origin.Y); + + // recursively go down the DrawingGroup to build up the geometry + CombineGeometryRecursive(drawing, ref transform, ref accumulatedGeometry); + + return accumulatedGeometry; + } + /// /// Draws the text object /// @@ -1284,6 +1324,93 @@ namespace Avalonia.Media } } + private void CombineGeometryRecursive(Drawing drawing, ref Transform? transform, ref GeometryGroup? accumulatedGeometry) + { + if (drawing is DrawingGroup group) + { + transform = group.Transform; + + if (group.Children is DrawingCollection children) + { + // recursively go down for DrawingGroup + foreach (var child in children) + { + CombineGeometryRecursive(child, ref transform, ref accumulatedGeometry); + } + } + } + else + { + if (drawing is GlyphRunDrawing glyphRunDrawing) + { + // process glyph run + var glyphRun = glyphRunDrawing.GlyphRun; + + if (glyphRun != null) + { + var glyphRunGeometry = glyphRun.BuildGeometry(); + + glyphRunGeometry.Transform = transform; + + if (accumulatedGeometry == null) + { + accumulatedGeometry = new GeometryGroup + { + FillRule = FillRule.NonZero + }; + } + + accumulatedGeometry.Children.Add(glyphRunGeometry); + } + } + else + { + if (drawing is GeometryDrawing geometryDrawing) + { + // process geometry (i.e. TextDecoration on the line) + var geometry = geometryDrawing.Geometry; + + if (geometry != null) + { + geometry.Transform = transform; + + if (geometry is LineGeometry lineGeometry) + { + // For TextDecoration drawn by DrawLine(), the geometry is a LineGeometry which has no + // bounding area. So this line won't show up. Work aroud it by increase the Bounding rect + // to be Pen's thickness + + var bounds = lineGeometry.Bounds; + + if (bounds.Height == 0) + { + bounds = bounds.WithHeight(geometryDrawing.Pen?.Thickness ?? 0); + } + else if (bounds.Width == 0) + { + bounds = bounds.WithWidth(geometryDrawing.Pen?.Thickness ?? 0); + } + + // convert the line geometry into a rectangle geometry + // we lost line cap info here + geometry = new RectangleGeometry(bounds); + } + + if (accumulatedGeometry == null) + { + accumulatedGeometry = new GeometryGroup + { + FillRule = FillRule.NonZero + }; + } + + accumulatedGeometry.Children.Add(geometry); + } + } + } + } + } + private CachedMetrics DrawAndCalculateMetrics(DrawingContext? drawingContext, Point drawingOffset, bool getBlackBoxMetrics) { var metrics = new CachedMetrics(); diff --git a/src/Avalonia.Base/Media/GeometryCollection.cs b/src/Avalonia.Base/Media/GeometryCollection.cs new file mode 100644 index 0000000000..2afa191dcf --- /dev/null +++ b/src/Avalonia.Base/Media/GeometryCollection.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using Avalonia.Collections; + +#nullable enable + +namespace Avalonia.Media +{ + public sealed class GeometryCollection : AvaloniaList + { + public GeometryCollection() + { + ResetBehavior = ResetBehavior.Remove; + + this.ForEachItem( + x => + { + Parent?.Invalidate(); + }, + x => + { + Parent?.Invalidate(); + }, + () => throw new NotSupportedException()); + } + + public GeometryCollection(IEnumerable items) : base(items) + { + ResetBehavior = ResetBehavior.Remove; + + this.ForEachItem( + x => + { + Parent?.Invalidate(); + }, + x => + { + Parent?.Invalidate(); + }, + () => throw new NotSupportedException()); + } + + public GeometryGroup? Parent { get; set; } + } +} diff --git a/src/Avalonia.Base/Media/GeometryDrawing.cs b/src/Avalonia.Base/Media/GeometryDrawing.cs index e46e3a7d5f..7df7d25954 100644 --- a/src/Avalonia.Base/Media/GeometryDrawing.cs +++ b/src/Avalonia.Base/Media/GeometryDrawing.cs @@ -21,14 +21,14 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty BrushProperty = - AvaloniaProperty.Register(nameof(Brush), Brushes.Transparent); + public static readonly StyledProperty BrushProperty = + AvaloniaProperty.Register(nameof(Brush), Brushes.Transparent); /// /// Defines the property. /// - public static readonly StyledProperty PenProperty = - AvaloniaProperty.Register(nameof(Pen)); + public static readonly StyledProperty PenProperty = + AvaloniaProperty.Register(nameof(Pen)); /// /// Gets or sets the that describes the shape of this . @@ -43,7 +43,7 @@ namespace Avalonia.Media /// /// Gets or sets the used to fill the interior of the shape described by this . /// - public IBrush Brush + public IBrush? Brush { get => GetValue(BrushProperty); set => SetValue(BrushProperty, value); @@ -52,7 +52,7 @@ namespace Avalonia.Media /// /// Gets or sets the used to stroke this . /// - public IPen Pen + public IPen? Pen { get => GetValue(PenProperty); set => SetValue(PenProperty, value); @@ -68,7 +68,8 @@ namespace Avalonia.Media public override Rect GetBounds() { - return Geometry?.GetRenderBounds(s_boundsPen) ?? Rect.Empty; + IPen pen = Pen ?? s_boundsPen; + return Geometry?.GetRenderBounds(pen) ?? Rect.Empty; } } } diff --git a/src/Avalonia.Base/GeometryGroup.cs b/src/Avalonia.Base/Media/GeometryGroup.cs similarity index 64% rename from src/Avalonia.Base/GeometryGroup.cs rename to src/Avalonia.Base/Media/GeometryGroup.cs index b90c9c6d8a..0326e606f4 100644 --- a/src/Avalonia.Base/GeometryGroup.cs +++ b/src/Avalonia.Base/Media/GeometryGroup.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Avalonia.Metadata; +using Avalonia.Metadata; using Avalonia.Platform; #nullable enable @@ -13,29 +10,36 @@ namespace Avalonia.Media /// public class GeometryGroup : Geometry { - public static readonly DirectProperty ChildrenProperty = - AvaloniaProperty.RegisterDirect ( + public static readonly DirectProperty ChildrenProperty = + AvaloniaProperty.RegisterDirect ( nameof(Children), o => o.Children, - (o, v) => o.Children = v); + (o, v)=> o.Children = v); public static readonly StyledProperty FillRuleProperty = AvaloniaProperty.Register(nameof(FillRule)); - private GeometryCollection? _children; - private bool _childrenSet; + private GeometryCollection _children; + + public GeometryGroup() + { + _children = new GeometryCollection + { + Parent = this + }; + } /// /// Gets or sets the collection that contains the child geometries. /// [Content] - public GeometryCollection? Children + public GeometryCollection Children { - get => _children ??= (!_childrenSet ? new GeometryCollection() : null); + get => _children; set { - SetAndRaise(ChildrenProperty, ref _children, value); - _childrenSet = true; + OnChildrenChanged(_children, value); + SetAndRaise(ChildrenProperty, ref _children, value); } } @@ -52,16 +56,28 @@ namespace Avalonia.Media public override Geometry Clone() { var result = new GeometryGroup { FillRule = FillRule, Transform = Transform }; - if (_children?.Count > 0) + + if (_children.Count > 0) + { result.Children = new GeometryCollection(_children); + } + return result; } + protected void OnChildrenChanged(GeometryCollection oldChildren, GeometryCollection newChildren) + { + oldChildren.Parent = null; + + newChildren.Parent = this; + } + protected override IGeometryImpl? CreateDefiningGeometry() { - if (_children?.Count > 0) + if (_children.Count > 0) { var factory = AvaloniaLocator.Current.GetRequiredService(); + return factory.CreateGeometryGroup(FillRule, _children); } @@ -72,10 +88,18 @@ namespace Avalonia.Media { base.OnPropertyChanged(change); - if (change.Property == ChildrenProperty || change.Property == FillRuleProperty) + switch (change.Property.Name) { - InvalidateGeometry(); + case nameof(FillRule): + case nameof(Children): + InvalidateGeometry(); + break; } } + + internal void Invalidate() + { + InvalidateGeometry(); + } } } diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index 22be8d8865..ac87d521a5 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -194,6 +194,19 @@ namespace Avalonia.Media } } + /// + /// Obtains geometry for the glyph run. + /// + /// The geometry returned contains the combined geometry of all glyphs in the glyph run. + public Geometry BuildGeometry() + { + var platformRenderInterface = AvaloniaLocator.Current.GetRequiredService(); + + var geometryImpl = platformRenderInterface.BuildGlyphRunGeometry(this); + + return new PlatformGeometry(geometryImpl); + } + /// /// Retrieves the offset from the leading edge of the /// to the leading or trailing edge of a caret stop containing the specified character hit. @@ -721,10 +734,9 @@ namespace Avalonia.Media private void Set(ref T field, T value) { - if (_glyphRunImpl != null) - { - throw new InvalidOperationException("GlyphRun can't be changed after it has been initialized.'"); - } + _glyphRunImpl?.Dispose(); + + _glyphRunImpl = null; _glyphRunMetrics = null; diff --git a/src/Avalonia.Base/Media/IBrush.cs b/src/Avalonia.Base/Media/IBrush.cs index 830c066182..e32894f67c 100644 --- a/src/Avalonia.Base/Media/IBrush.cs +++ b/src/Avalonia.Base/Media/IBrush.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using Avalonia.Metadata; namespace Avalonia.Media { @@ -6,6 +7,7 @@ namespace Avalonia.Media /// Describes how an area is painted. /// [TypeConverter(typeof(BrushConverter))] + [NotClientImplementable] public interface IBrush { /// @@ -17,5 +19,10 @@ namespace Avalonia.Media /// Gets the transform of the brush. /// ITransform? Transform { get; } + + /// + /// Gets the origin of the brushes + /// + RelativePoint TransformOrigin { get; } } } diff --git a/src/Avalonia.Base/Media/IConicGradientBrush.cs b/src/Avalonia.Base/Media/IConicGradientBrush.cs index 5368dd1851..6a397b86d4 100644 --- a/src/Avalonia.Base/Media/IConicGradientBrush.cs +++ b/src/Avalonia.Base/Media/IConicGradientBrush.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Paints an area with a conic gradient. /// + [NotClientImplementable] public interface IConicGradientBrush : IGradientBrush { /// diff --git a/src/Avalonia.Base/Media/IDashStyle.cs b/src/Avalonia.Base/Media/IDashStyle.cs index 7835c7a1e9..7208216603 100644 --- a/src/Avalonia.Base/Media/IDashStyle.cs +++ b/src/Avalonia.Base/Media/IDashStyle.cs @@ -1,10 +1,12 @@ using System.Collections.Generic; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// Represents the sequence of dashes and gaps that will be applied by a . /// + [NotClientImplementable] public interface IDashStyle { /// diff --git a/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs index e71584258a..38048fb255 100644 --- a/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs +++ b/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Experimental Interface for producing Acrylic-like materials. /// + [NotClientImplementable] public interface IExperimentalAcrylicMaterial { /// diff --git a/src/Avalonia.Base/Media/IGradientBrush.cs b/src/Avalonia.Base/Media/IGradientBrush.cs index 18db0af660..9b78a4af78 100644 --- a/src/Avalonia.Base/Media/IGradientBrush.cs +++ b/src/Avalonia.Base/Media/IGradientBrush.cs @@ -1,10 +1,12 @@ using System.Collections.Generic; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// A brush that draws with a gradient. /// + [NotClientImplementable] public interface IGradientBrush : IBrush { /// diff --git a/src/Avalonia.Base/Media/IGradientStop.cs b/src/Avalonia.Base/Media/IGradientStop.cs index 22eb9df60d..64b952f3bc 100644 --- a/src/Avalonia.Base/Media/IGradientStop.cs +++ b/src/Avalonia.Base/Media/IGradientStop.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Describes the location and color of a transition point in a gradient. /// + [NotClientImplementable] public interface IGradientStop { /// diff --git a/src/Avalonia.Base/Media/IImageBrush.cs b/src/Avalonia.Base/Media/IImageBrush.cs index aaa481bd28..732f1957d0 100644 --- a/src/Avalonia.Base/Media/IImageBrush.cs +++ b/src/Avalonia.Base/Media/IImageBrush.cs @@ -1,10 +1,12 @@ using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// Paints an area with an . /// + [NotClientImplementable] public interface IImageBrush : ITileBrush { /// @@ -12,4 +14,4 @@ namespace Avalonia.Media /// IBitmap Source { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/ILinearGradientBrush.cs b/src/Avalonia.Base/Media/ILinearGradientBrush.cs index 3e2a5a0e22..4f4a55db36 100644 --- a/src/Avalonia.Base/Media/ILinearGradientBrush.cs +++ b/src/Avalonia.Base/Media/ILinearGradientBrush.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// A brush that draws with a linear gradient. /// + [NotClientImplementable] public interface ILinearGradientBrush : IGradientBrush { /// @@ -15,4 +18,4 @@ /// RelativePoint EndPoint { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/IMutableBrush.cs b/src/Avalonia.Base/Media/IMutableBrush.cs index 415db61d68..fef124ba36 100644 --- a/src/Avalonia.Base/Media/IMutableBrush.cs +++ b/src/Avalonia.Base/Media/IMutableBrush.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// Represents a mutable brush which can return an immutable clone of itself. /// + [NotClientImplementable] public interface IMutableBrush : IBrush, IAffectsRender { /// diff --git a/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs index fcfe4631a6..f954a8c52a 100644 --- a/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs +++ b/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Represents a mutable brush which can return an immutable clone of itself. /// + [NotClientImplementable] public interface IMutableExperimentalAcrylicMaterial : IExperimentalAcrylicMaterial, IAffectsRender { /// diff --git a/src/Avalonia.Base/Media/IMutableTransform.cs b/src/Avalonia.Base/Media/IMutableTransform.cs index 2033c434c0..22526eed17 100644 --- a/src/Avalonia.Base/Media/IMutableTransform.cs +++ b/src/Avalonia.Base/Media/IMutableTransform.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Media { diff --git a/src/Avalonia.Base/Media/IPen.cs b/src/Avalonia.Base/Media/IPen.cs index 1cad9948b4..eca1cb507a 100644 --- a/src/Avalonia.Base/Media/IPen.cs +++ b/src/Avalonia.Base/Media/IPen.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Describes how a stroke is drawn. /// + [NotClientImplementable] public interface IPen { /// diff --git a/src/Avalonia.Base/Media/IRadialGradientBrush.cs b/src/Avalonia.Base/Media/IRadialGradientBrush.cs index cadf53cc18..0f025e98bc 100644 --- a/src/Avalonia.Base/Media/IRadialGradientBrush.cs +++ b/src/Avalonia.Base/Media/IRadialGradientBrush.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Paints an area with a radial gradient. /// + [NotClientImplementable] public interface IRadialGradientBrush : IGradientBrush { /// @@ -21,4 +24,4 @@ /// double Radius { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/ISolidColorBrush.cs b/src/Avalonia.Base/Media/ISolidColorBrush.cs index d0ab00599b..29e11210f1 100644 --- a/src/Avalonia.Base/Media/ISolidColorBrush.cs +++ b/src/Avalonia.Base/Media/ISolidColorBrush.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Media { /// /// Fills an area with a solid color. /// + [NotClientImplementable] public interface ISolidColorBrush : IBrush { /// @@ -10,4 +13,4 @@ namespace Avalonia.Media /// Color Color { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/ITileBrush.cs b/src/Avalonia.Base/Media/ITileBrush.cs index 991857eec9..cb5a591003 100644 --- a/src/Avalonia.Base/Media/ITileBrush.cs +++ b/src/Avalonia.Base/Media/ITileBrush.cs @@ -1,10 +1,12 @@ using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Media -{ +{ /// /// A brush which displays a repeating image. /// + [NotClientImplementable] public interface ITileBrush : IBrush { /// diff --git a/src/Avalonia.Base/Media/IVisualBrush.cs b/src/Avalonia.Base/Media/IVisualBrush.cs index e74892b218..b8900bbd73 100644 --- a/src/Avalonia.Base/Media/IVisualBrush.cs +++ b/src/Avalonia.Base/Media/IVisualBrush.cs @@ -1,10 +1,12 @@ -using Avalonia.VisualTree; +using Avalonia.Metadata; +using Avalonia.VisualTree; namespace Avalonia.Media { /// /// Paints an area with an . /// + [NotClientImplementable] public interface IVisualBrush : ITileBrush { /// @@ -12,4 +14,4 @@ namespace Avalonia.Media /// IVisual Visual { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/Imaging/IBitmap.cs b/src/Avalonia.Base/Media/Imaging/IBitmap.cs index 134bebc002..bd04d5ce86 100644 --- a/src/Avalonia.Base/Media/Imaging/IBitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/IBitmap.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Utilities; @@ -8,6 +9,7 @@ namespace Avalonia.Media.Imaging /// /// Represents a bitmap image. /// + [NotClientImplementable] public interface IBitmap : IImage, IDisposable { /// diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs index 4b97615c4c..70232f0a63 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs @@ -13,6 +13,7 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. /// The center point for the gradient. /// The starting angle for the gradient. @@ -20,10 +21,11 @@ namespace Avalonia.Media.Immutable IReadOnlyList gradientStops, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint? transformOrigin = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, double angle = 0) - : base(gradientStops, opacity, transform, spreadMethod) + : base(gradientStops, opacity, transform, transformOrigin, spreadMethod) { Center = center ?? RelativePoint.Center; Angle = angle; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs index f1e51687d0..1e95acbf22 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs @@ -13,16 +13,19 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. protected ImmutableGradientBrush( IReadOnlyList gradientStops, double opacity, ImmutableTransform? transform, + RelativePoint? transformOrigin, GradientSpreadMethod spreadMethod) { GradientStops = gradientStops; Opacity = opacity; Transform = transform; + TransformOrigin = transformOrigin.HasValue ? transformOrigin.Value : RelativePoint.TopLeft; SpreadMethod = spreadMethod; } @@ -31,7 +34,8 @@ namespace Avalonia.Media.Immutable /// /// The brush from which this brush's properties should be copied. protected ImmutableGradientBrush(GradientBrush source) - : this(source.GradientStops.ToImmutable(), source.Opacity, source.Transform?.ToImmutable(), source.SpreadMethod) + : this(source.GradientStops.ToImmutable(), source.Opacity, source.Transform?.ToImmutable(), + source.TransformOrigin, source.SpreadMethod) { } @@ -47,6 +51,11 @@ namespace Avalonia.Media.Immutable /// public ITransform? Transform { get; } + /// + /// Gets the transform origin of the brush + /// + public RelativePoint TransformOrigin { get; } + /// public GradientSpreadMethod SpreadMethod { get; } } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs index c36e82eacb..f9892bf60c 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs @@ -16,6 +16,7 @@ namespace Avalonia.Media.Immutable /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The rectangle of the source image that will be displayed. /// /// How the source rectangle will be stretched to fill the destination rect. @@ -29,6 +30,7 @@ namespace Avalonia.Media.Immutable RelativeRect? destinationRect = null, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint transformOrigin = new RelativePoint(), RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, @@ -39,6 +41,7 @@ namespace Avalonia.Media.Immutable destinationRect ?? RelativeRect.Fill, opacity, transform, + transformOrigin, sourceRect ?? RelativeRect.Fill, stretch, tileMode, diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs index 64c0f9b44e..3c26b5c009 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs @@ -13,6 +13,7 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. /// The start point for the gradient. /// The end point for the gradient. @@ -20,10 +21,11 @@ namespace Avalonia.Media.Immutable IReadOnlyList gradientStops, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint? transformOrigin = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? startPoint = null, RelativePoint? endPoint = null) - : base(gradientStops, opacity, transform, spreadMethod) + : base(gradientStops, opacity, transform, transformOrigin, spreadMethod) { StartPoint = startPoint ?? RelativePoint.TopLeft; EndPoint = endPoint ?? RelativePoint.BottomRight; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs index 3da4bdd8e9..e08d2810f8 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs @@ -13,6 +13,7 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. /// The start point for the gradient. /// @@ -25,11 +26,12 @@ namespace Avalonia.Media.Immutable IReadOnlyList gradientStops, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint? transformOrigin = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, RelativePoint? gradientOrigin = null, double radius = 0.5) - : base(gradientStops, opacity, transform, spreadMethod) + : base(gradientStops, opacity, transform, transformOrigin, spreadMethod) { Center = center ?? RelativePoint.Center; GradientOrigin = gradientOrigin ?? RelativePoint.Center; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs index 9b1b2500ef..6755dfd236 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs @@ -53,6 +53,11 @@ namespace Avalonia.Media.Immutable /// public ITransform? Transform { get; } + /// + /// Gets the transform origin of the brush + /// + public RelativePoint TransformOrigin { get; } + public bool Equals(ImmutableSolidColorBrush? other) { if (ReferenceEquals(null, other)) return false; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs index 2c1844a2c2..6df27872fc 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs @@ -15,6 +15,7 @@ namespace Avalonia.Media.Immutable /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The rectangle of the source image that will be displayed. /// /// How the source rectangle will be stretched to fill the destination rect. @@ -27,6 +28,7 @@ namespace Avalonia.Media.Immutable RelativeRect destinationRect, double opacity, ImmutableTransform? transform, + RelativePoint transformOrigin, RelativeRect sourceRect, Stretch stretch, TileMode tileMode, @@ -37,6 +39,7 @@ namespace Avalonia.Media.Immutable DestinationRect = destinationRect; Opacity = opacity; Transform = transform; + TransformOrigin = transformOrigin; SourceRect = sourceRect; Stretch = stretch; TileMode = tileMode; @@ -54,6 +57,7 @@ namespace Avalonia.Media.Immutable source.DestinationRect, source.Opacity, source.Transform?.ToImmutable(), + source.TransformOrigin, source.SourceRect, source.Stretch, source.TileMode, @@ -78,6 +82,11 @@ namespace Avalonia.Media.Immutable /// public ITransform? Transform { get; } + /// + /// Gets the transform origin of the brush + /// + public RelativePoint TransformOrigin { get; } + /// public RelativeRect SourceRect { get; } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs index 8ecef63237..b436dcdb5e 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs @@ -17,6 +17,7 @@ namespace Avalonia.Media.Immutable /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The rectangle of the source image that will be displayed. /// /// How the source rectangle will be stretched to fill the destination rect. @@ -30,6 +31,7 @@ namespace Avalonia.Media.Immutable RelativeRect? destinationRect = null, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint transformOrigin = new RelativePoint(), RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, @@ -40,6 +42,7 @@ namespace Avalonia.Media.Immutable destinationRect ?? RelativeRect.Fill, opacity, transform, + transformOrigin, sourceRect ?? RelativeRect.Fill, stretch, tileMode, diff --git a/src/Avalonia.Base/Media/PlatformGeometry.cs b/src/Avalonia.Base/Media/PlatformGeometry.cs new file mode 100644 index 0000000000..f25a14540f --- /dev/null +++ b/src/Avalonia.Base/Media/PlatformGeometry.cs @@ -0,0 +1,24 @@ +using Avalonia.Platform; + +namespace Avalonia.Media +{ + internal class PlatformGeometry : Geometry + { + private readonly IGeometryImpl _geometryImpl; + + public PlatformGeometry(IGeometryImpl geometryImpl) + { + _geometryImpl = geometryImpl; + } + + public override Geometry Clone() + { + return new PlatformGeometry(_geometryImpl); + } + + protected override IGeometryImpl? CreateDefiningGeometry() + { + return _geometryImpl; + } + } +} diff --git a/src/Avalonia.Base/Media/TextAlignment.cs b/src/Avalonia.Base/Media/TextAlignment.cs index b1a394e157..94416ccde2 100644 --- a/src/Avalonia.Base/Media/TextAlignment.cs +++ b/src/Avalonia.Base/Media/TextAlignment.cs @@ -19,5 +19,28 @@ namespace Avalonia.Media /// The text is right-aligned. /// Right, + + /// + /// The beginning of the text is aligned to the edge of the available space. + /// + Start, + + /// + /// The end of the text is aligned to the edge of the available space. + /// + End, + + /// + /// Text alignment is inferred from the text content. + /// + /// + /// When the TextAlignment property is set to DetectFromContent, alignment is inferred from the text content of the control. For example, English text is left aligned, and Arabic text is right aligned. + /// + DetectFromContent, + + /// + /// Text is justified within the available space. + /// + Justify } } diff --git a/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs b/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs index 32012ab8e9..26966b37bc 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Media.TextFormatting +using Avalonia.Metadata; + +namespace Avalonia.Media.TextFormatting { /// /// Produces objects that are used by the . diff --git a/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs new file mode 100644 index 0000000000..df83ada34a --- /dev/null +++ b/src/Avalonia.Base/Media/TextFormatting/InterWordJustification.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using Avalonia.Media.TextFormatting.Unicode; + +namespace Avalonia.Media.TextFormatting +{ + internal class InterWordJustification : JustificationProperties + { + public InterWordJustification(double width) + { + Width = width; + } + + public override double Width { get; } + + public override void Justify(TextLine textLine) + { + var paragraphWidth = Width; + + if (double.IsInfinity(paragraphWidth)) + { + return; + } + + if (textLine.NewLineLength > 0) + { + return; + } + + var textLineBreak = textLine.TextLineBreak; + + if (textLineBreak is not null && textLineBreak.TextEndOfLine is not null) + { + if (textLineBreak.RemainingRuns is null || textLineBreak.RemainingRuns.Count == 0) + { + return; + } + } + + var breakOportunities = new Queue(); + + foreach (var textRun in textLine.TextRuns) + { + var text = textRun.Text; + + if (text.IsEmpty) + { + continue; + } + + var start = text.Start; + + var lineBreakEnumerator = new LineBreakEnumerator(text); + + while (lineBreakEnumerator.MoveNext()) + { + var currentBreak = lineBreakEnumerator.Current; + + if (!currentBreak.Required && currentBreak.PositionWrap != text.Length) + { + breakOportunities.Enqueue(start + currentBreak.PositionMeasure); + } + } + } + + if (breakOportunities.Count == 0) + { + return; + } + + var remainingSpace = Math.Max(0, paragraphWidth - textLine.WidthIncludingTrailingWhitespace); + var spacing = remainingSpace / breakOportunities.Count; + + foreach (var textRun in textLine.TextRuns) + { + var text = textRun.Text; + + if (text.IsEmpty) + { + continue; + } + + if (textRun is ShapedTextCharacters shapedText) + { + var glyphRun = shapedText.GlyphRun; + var shapedBuffer = shapedText.ShapedBuffer; + var currentPosition = text.Start; + + while (breakOportunities.Count > 0) + { + var characterIndex = breakOportunities.Dequeue(); + + if (characterIndex < currentPosition) + { + continue; + } + + var glyphIndex = glyphRun.FindGlyphIndex(characterIndex); + var glyphInfo = shapedBuffer.GlyphInfos[glyphIndex]; + + shapedBuffer.GlyphInfos[glyphIndex] = new GlyphInfo(glyphInfo.GlyphIndex, glyphInfo.GlyphCluster, glyphInfo.GlyphAdvance + spacing); + } + + glyphRun.GlyphAdvances = shapedBuffer.GlyphAdvances; + } + } + } + } +} diff --git a/src/Avalonia.Base/Media/TextFormatting/JustificationProperties.cs b/src/Avalonia.Base/Media/TextFormatting/JustificationProperties.cs new file mode 100644 index 0000000000..620ad17189 --- /dev/null +++ b/src/Avalonia.Base/Media/TextFormatting/JustificationProperties.cs @@ -0,0 +1,16 @@ +namespace Avalonia.Media.TextFormatting +{ + public abstract class JustificationProperties + { + /// + /// Gets the width in which the range is justified. + /// + public abstract double Width { get; } + + /// + /// Justifies given text line. + /// + /// Text line to collapse. + public abstract void Justify(TextLine textLine); + } +} diff --git a/src/Avalonia.Base/Media/TextFormatting/TextBounds.cs b/src/Avalonia.Base/Media/TextFormatting/TextBounds.cs index a0b51671f0..93edf68348 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextBounds.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextBounds.cs @@ -10,20 +10,27 @@ namespace Avalonia.Media.TextFormatting /// /// Constructing TextBounds object /// - internal TextBounds(Rect bounds, FlowDirection flowDirection) + internal TextBounds(Rect bounds, FlowDirection flowDirection, IList runBounds) { Rectangle = bounds; FlowDirection = flowDirection; + TextRunBounds = runBounds; } /// /// Bounds rectangle /// - public Rect Rectangle { get; } + public Rect Rectangle { get; internal set; } /// /// Text flow direction inside the boundary rectangle /// public FlowDirection FlowDirection { get; } + + /// + /// Get a list of run bounding rectangles + /// + /// Array of text run bounds + public IList TextRunBounds { get; } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs index 4205268bc6..16caadb0dd 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs @@ -15,7 +15,7 @@ namespace Avalonia.Media.TextFormatting TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null) { var textWrapping = paragraphProperties.TextWrapping; - FlowDirection flowDirection; + FlowDirection resolvedFlowDirection; TextLineBreak? nextLineBreak = null; List drawableTextRuns; @@ -24,17 +24,17 @@ namespace Avalonia.Media.TextFormatting if (previousLineBreak?.RemainingRuns != null) { - flowDirection = previousLineBreak.FlowDirection; + resolvedFlowDirection = previousLineBreak.FlowDirection; drawableTextRuns = previousLineBreak.RemainingRuns.ToList(); nextLineBreak = previousLineBreak; } else { - drawableTextRuns = ShapeTextRuns(textRuns, paragraphProperties, out flowDirection); + drawableTextRuns = ShapeTextRuns(textRuns, paragraphProperties, out resolvedFlowDirection); if (nextLineBreak == null && textEndOfLine != null) { - nextLineBreak = new TextLineBreak(textEndOfLine, flowDirection); + nextLineBreak = new TextLineBreak(textEndOfLine, resolvedFlowDirection); } } @@ -45,7 +45,7 @@ namespace Avalonia.Media.TextFormatting case TextWrapping.NoWrap: { textLine = new TextLineImpl(drawableTextRuns, firstTextSourceIndex, textSourceLength, - paragraphWidth, paragraphProperties, flowDirection, nextLineBreak); + paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak); textLine.FinalizeLine(); @@ -55,7 +55,7 @@ namespace Avalonia.Media.TextFormatting case TextWrapping.Wrap: { textLine = PerformTextWrapping(drawableTextRuns, firstTextSourceIndex, paragraphWidth, paragraphProperties, - flowDirection, nextLineBreak); + resolvedFlowDirection, nextLineBreak); break; } default: @@ -404,9 +404,9 @@ namespace Avalonia.Media.TextFormatting { endOfLine = textEndOfLine; - textRuns.Add(textRun); + textSourceLength += textEndOfLine.TextSourceLength; - textSourceLength += textRun.TextSourceLength; + textRuns.Add(textRun); break; } @@ -431,9 +431,9 @@ namespace Avalonia.Media.TextFormatting break; } - case DrawableTextRun drawableTextRun: + default: { - textRuns.Add(drawableTextRun); + textRuns.Add(textRun); break; } } @@ -552,11 +552,11 @@ namespace Avalonia.Media.TextFormatting /// The first text source index. /// The paragraph width. /// The text paragraph properties. - /// + /// /// The current line break if the line was explicitly broken. /// The wrapped text line. private static TextLineImpl PerformTextWrapping(List textRuns, int firstTextSourceIndex, - double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection flowDirection, + double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection resolvedFlowDirection, TextLineBreak? currentLineBreak) { if(textRuns.Count == 0) @@ -684,16 +684,16 @@ namespace Avalonia.Media.TextFormatting var remainingCharacters = splitResult.Second; var lineBreak = remainingCharacters?.Count > 0 ? - new TextLineBreak(currentLineBreak?.TextEndOfLine, flowDirection, remainingCharacters) : + new TextLineBreak(currentLineBreak?.TextEndOfLine, resolvedFlowDirection, remainingCharacters) : null; if (lineBreak is null && currentLineBreak?.TextEndOfLine != null) { - lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine, flowDirection); + lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine, resolvedFlowDirection); } var textLine = new TextLineImpl(splitResult.First, firstTextSourceIndex, measuredLength, - paragraphWidth, paragraphProperties, flowDirection, + paragraphWidth, paragraphProperties, resolvedFlowDirection, lineBreak); return textLine.FinalizeLine(); diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs index 5d5d45db2d..f3af240c58 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs @@ -230,7 +230,7 @@ namespace Avalonia.Media.TextFormatting foreach (var textLine in TextLines) { //Current line isn't covered. - if (textLine.FirstTextSourceIndex + textLine.Length <= start) + if (textLine.FirstTextSourceIndex + textLine.Length < start) { currentY += textLine.Height; @@ -239,18 +239,27 @@ namespace Avalonia.Media.TextFormatting var textBounds = textLine.GetTextBounds(start, length); - foreach (var bounds in textBounds) + if(textBounds.Count > 0) { - Rect? last = result.Count > 0 ? result[result.Count - 1] : null; - - if (last.HasValue && MathUtilities.AreClose(last.Value.Right, bounds.Rectangle.Left) && MathUtilities.AreClose(last.Value.Top, currentY)) + foreach (var bounds in textBounds) { - result[result.Count - 1] = last.Value.WithWidth(last.Value.Width + bounds.Rectangle.Width); + Rect? last = result.Count > 0 ? result[result.Count - 1] : null; + + if (last.HasValue && MathUtilities.AreClose(last.Value.Right, bounds.Rectangle.Left) && MathUtilities.AreClose(last.Value.Top, currentY)) + { + result[result.Count - 1] = last.Value.WithWidth(last.Value.Width + bounds.Rectangle.Width); + } + else + { + result.Add(bounds.Rectangle.WithY(currentY)); + } + + foreach (var runBounds in bounds.TextRunBounds) + { + start += runBounds.Length; + length -= runBounds.Length; + } } - else - { - result.Add(bounds.Rectangle.WithY(currentY)); - } } if(textLine.FirstTextSourceIndex + textLine.Length >= start + length) @@ -430,7 +439,7 @@ namespace Avalonia.Media.TextFormatting var textLine = TextFormatter.Current.FormatLine(_textSource, _textSourceLength, MaxWidth, _paragraphProperties, previousLine?.TextLineBreak); - if(textLine == null || textLine.Length == 0) + if(textLine == null || textLine.Length == 0 || textLine.TextRuns.Count == 0 && textLine.TextLineBreak?.TextEndOfLine is TextEndOfParagraph) { if(previousLine != null && previousLine.NewLineLength > 0) { @@ -492,6 +501,35 @@ namespace Avalonia.Media.TextFormatting Bounds = new Rect(left, 0, width, height); + if(_paragraphProperties.TextAlignment == TextAlignment.Justify) + { + var whitespaceWidth = 0d; + + foreach (var line in textLines) + { + var lineWhitespaceWidth = line.Width - line.WidthIncludingTrailingWhitespace; + + if(lineWhitespaceWidth > whitespaceWidth) + { + whitespaceWidth = lineWhitespaceWidth; + } + } + + var justificationWidth = width - whitespaceWidth; + + if(justificationWidth > 0) + { + var justificationProperties = new InterWordJustification(justificationWidth); + + for (var i = 0; i < textLines.Count - 1; i++) + { + var line = textLines[i]; + + line.Justify(justificationProperties); + } + } + } + return textLines; } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLine.cs b/src/Avalonia.Base/Media/TextFormatting/TextLine.cs index 1f69c15acc..c8a23097db 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLine.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLine.cs @@ -15,9 +15,15 @@ namespace Avalonia.Media.TextFormatting /// The contained text runs. /// public abstract IReadOnlyList TextRuns { get; } - + + /// + /// Gets the first TextSource position of the current line. + /// public abstract int FirstTextSourceIndex { get; } + /// + /// Gets the total number of TextSource positions of the current line. + /// public abstract int Length { get; } /// @@ -56,7 +62,7 @@ namespace Avalonia.Media.TextFormatting /// Gets a value that indicates whether content of the line overflows the specified paragraph width. /// /// - /// true, it the line overflows the specified paragraph width; otherwise, false. + /// true, the line overflows the specified paragraph width; otherwise, false. /// public abstract bool HasOverflowed { get; } @@ -75,7 +81,7 @@ namespace Avalonia.Media.TextFormatting /// The number of newline characters. /// public abstract int NewLineLength { get; } - + /// /// Gets the distance that black pixels extend beyond the bottom alignment edge of a line. /// @@ -149,6 +155,15 @@ namespace Avalonia.Media.TextFormatting /// public abstract TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList); + /// + /// Create a justified line based on justification text properties. + /// + /// An object that represent the justification text properties. + /// + /// A value that represents a justified line that can be displayed. + /// + public abstract void Justify(JustificationProperties justificationProperties); + /// /// Gets the character hit corresponding to the specified distance from the beginning of the line. /// @@ -192,50 +207,5 @@ namespace Avalonia.Media.TextFormatting /// number of characters of the specified range /// an array of bounding rectangles. public abstract IReadOnlyList GetTextBounds(int firstTextSourceCharacterIndex, int textLength); - - /// - /// Gets the text line offset x. - /// - /// The line width. - /// The paragraph width including whitespace. - /// The paragraph width. - /// The text alignment. - /// The flow direction of the line. - /// The paragraph offset. - internal static double GetParagraphOffsetX(double width, double widthIncludingTrailingWhitespace, - double paragraphWidth, TextAlignment textAlignment, FlowDirection flowDirection) - { - if (double.IsPositiveInfinity(paragraphWidth)) - { - return 0; - } - - if (flowDirection == FlowDirection.LeftToRight) - { - switch (textAlignment) - { - case TextAlignment.Center: - return Math.Max(0, (paragraphWidth - width) / 2); - - case TextAlignment.Right: - return Math.Max(0, paragraphWidth - widthIncludingTrailingWhitespace); - - default: - return 0; - } - } - - switch (textAlignment) - { - case TextAlignment.Center: - return Math.Max(0, (paragraphWidth - width) / 2); - - case TextAlignment.Right: - return 0; - - default: - return Math.Max(0, paragraphWidth - widthIncludingTrailingWhitespace); - } - } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 73ec055bbe..7c686358e2 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -10,10 +10,10 @@ namespace Avalonia.Media.TextFormatting private readonly double _paragraphWidth; private readonly TextParagraphProperties _paragraphProperties; private TextLineMetrics _textLineMetrics; - private readonly FlowDirection _flowDirection; + private readonly FlowDirection _resolvedFlowDirection; public TextLineImpl(List textRuns, int firstTextSourceIndex, int length, double paragraphWidth, - TextParagraphProperties paragraphProperties, FlowDirection flowDirection = FlowDirection.LeftToRight, + TextParagraphProperties paragraphProperties, FlowDirection resolvedFlowDirection = FlowDirection.LeftToRight, TextLineBreak? lineBreak = null, bool hasCollapsed = false) { FirstTextSourceIndex = firstTextSourceIndex; @@ -25,7 +25,7 @@ namespace Avalonia.Media.TextFormatting _paragraphWidth = paragraphWidth; _paragraphProperties = paragraphProperties; - _flowDirection = flowDirection; + _resolvedFlowDirection = resolvedFlowDirection; } /// @@ -136,7 +136,7 @@ namespace Avalonia.Media.TextFormatting } var collapsedLine = new TextLineImpl(collapsedRuns, FirstTextSourceIndex, Length, _paragraphWidth, _paragraphProperties, - _flowDirection, TextLineBreak, true); + _resolvedFlowDirection, TextLineBreak, true); if (collapsedRuns.Count > 0) { @@ -144,7 +144,14 @@ namespace Avalonia.Media.TextFormatting } return collapsedLine; + } + + /// + public override void Justify(JustificationProperties justificationProperties) + { + justificationProperties.Justify(this); + _textLineMetrics = CreateLineMetrics(); } /// @@ -167,7 +174,7 @@ namespace Avalonia.Media.TextFormatting return shapedTextCharacters.GlyphRun.GetCharacterHitFromDistance(distance, out _); } - return _flowDirection == FlowDirection.LeftToRight ? + return _resolvedFlowDirection == FlowDirection.LeftToRight ? new CharacterHit(FirstTextSourceIndex) : new CharacterHit(FirstTextSourceIndex + Length); } @@ -184,6 +191,10 @@ namespace Avalonia.Media.TextFormatting { characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _); + var offset = Math.Max(0, currentPosition - shapedRun.Text.Start); + + characterHit = new CharacterHit(characterHit.FirstCharacterIndex + offset, characterHit.TrailingLength); + break; } default: @@ -215,9 +226,11 @@ namespace Avalonia.Media.TextFormatting /// public override double GetDistanceFromCharacterHit(CharacterHit characterHit) { - var characterIndex = characterHit.FirstCharacterIndex + (characterHit.TrailingLength != 0 ? 1 : 0); + var isTrailingHit = characterHit.TrailingLength > 0; + var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; var currentDistance = Start; var currentPosition = FirstTextSourceIndex; + var remainingLength = characterIndex - FirstTextSourceIndex; GlyphRun? lastRun = null; @@ -242,8 +255,10 @@ namespace Avalonia.Media.TextFormatting } //Look for a hit in within the current run - if (characterIndex >= textRun.Text.Start && characterIndex <= textRun.Text.Start + textRun.Text.Length) + if (currentPosition + remainingLength <= currentPosition + textRun.Text.Length) { + characterHit = new CharacterHit(textRun.Text.Start + remainingLength); + var distance = currentRun.GetDistanceFromCharacterHit(characterHit); return currentDistance + distance; @@ -252,30 +267,29 @@ namespace Avalonia.Media.TextFormatting //Look at the left and right edge of the current run if (currentRun.IsLeftToRight) { - if (_flowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight)) + if (_resolvedFlowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight)) { - if (characterIndex <= textRun.Text.Start) + if (characterIndex <= currentPosition) { return currentDistance; } } else { - if (characterIndex == textRun.Text.Start) + if (characterIndex == currentPosition) { return currentDistance; } } - if (characterIndex == textRun.Text.Start + textRun.Text.Length && - characterHit.TrailingLength > 0) + if (characterIndex == currentPosition + textRun.Text.Length && isTrailingHit) { return currentDistance + currentRun.Size.Width; } } else { - if (characterIndex == textRun.Text.Start) + if (characterIndex == currentPosition) { return currentDistance + currentRun.Size.Width; } @@ -286,20 +300,24 @@ namespace Avalonia.Media.TextFormatting if (nextRun != null) { - if (characterHit.FirstCharacterIndex == textRun.Text.End && - nextRun.ShapedBuffer.IsLeftToRight) + if (nextRun.ShapedBuffer.IsLeftToRight) { - return currentDistance; + if (characterIndex == currentPosition + textRun.Text.Length) + { + return currentDistance; + } } - - if (characterIndex > textRun.Text.End && nextRun.Text.End < textRun.Text.End) + else { - return currentDistance; + if (currentPosition + nextRun.Text.Length == characterIndex) + { + return currentDistance; + } } } else { - if (characterIndex > textRun.Text.End) + if (characterIndex > currentPosition + textRun.Text.Length) { return currentDistance; } @@ -329,6 +347,12 @@ namespace Avalonia.Media.TextFormatting //No hit hit found so we add the full width currentDistance += textRun.Size.Width; currentPosition += textRun.TextSourceLength; + remainingLength -= textRun.TextSourceLength; + + if (remainingLength <= 0) + { + break; + } } return currentDistance; @@ -394,210 +418,299 @@ namespace Avalonia.Media.TextFormatting return GetPreviousCaretCharacterHit(characterHit); } - public override IReadOnlyList GetTextBounds(int firstTextSourceCharacterIndex, int textLength) + private IReadOnlyList GetTextBoundsLeftToRight(int firstTextSourceIndex, int textLength) { - if (firstTextSourceCharacterIndex + textLength <= FirstTextSourceIndex) - { - return Array.Empty(); - } + var characterIndex = firstTextSourceIndex + textLength; var result = new List(TextRuns.Count); - var lastDirection = _flowDirection; + var lastDirection = FlowDirection.LeftToRight; var currentDirection = lastDirection; + var currentPosition = FirstTextSourceIndex; - var currentRect = Rect.Empty; + var remainingLength = textLength; + var startX = Start; + double currentWidth = 0; + var currentRect = Rect.Empty; - //A portion of the line is covered. for (var index = 0; index < TextRuns.Count; index++) { - var currentRun = TextRuns[index] as DrawableTextRun; - - if (currentRun is null) + if (TextRuns[index] is not DrawableTextRun currentRun) { continue; } - TextRun? nextRun = null; - - if (index + 1 < TextRuns.Count) + if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex) { - nextRun = TextRuns[index + 1]; + startX += currentRun.Size.Width; + + currentPosition += currentRun.TextSourceLength; + + continue; } - if (nextRun != null) + var characterLength = 0; + var endX = startX; + + if (currentRun is ShapedTextCharacters currentShapedRun) { - switch (nextRun) - { - case ShapedTextCharacters when currentRun is ShapedTextCharacters: - { - if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End) - { - goto skip; - } + var offset = Math.Max(0, firstTextSourceIndex - currentPosition); - if (currentRun.Text.Start >= firstTextSourceCharacterIndex + textLength) - { - goto skip; - } + currentPosition += offset; - if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < firstTextSourceCharacterIndex) - { - goto skip; - } + var startIndex = currentRun.Text.Start + offset; - if (currentRun.Text.End < firstTextSourceCharacterIndex) - { - goto skip; - } + var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( + currentShapedRun.ShapedBuffer.IsLeftToRight ? + new CharacterHit(startIndex + remainingLength) : + new CharacterHit(startIndex)); - goto noop; - } - default: - { - goto noop; - } - } + endX += endOffset; + + var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( + currentShapedRun.ShapedBuffer.IsLeftToRight ? + new CharacterHit(startIndex) : + new CharacterHit(startIndex + remainingLength)); + + startX += startOffset; - skip: + var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); + var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); + + characterLength = Math.Abs(endHit.FirstCharacterIndex + endHit.TrailingLength - startHit.FirstCharacterIndex - startHit.TrailingLength); + + currentDirection = currentShapedRun.ShapedBuffer.IsLeftToRight ? + FlowDirection.LeftToRight : + FlowDirection.RightToLeft; + } + else + { + if (currentPosition < firstTextSourceIndex) { startX += currentRun.Size.Width; - currentPosition += currentRun.TextSourceLength; } - continue; - - noop: + if (currentPosition + currentRun.TextSourceLength <= characterIndex) { + endX += currentRun.Size.Width; + + characterLength = currentRun.TextSourceLength; } } - var endX = startX; - var endOffset = 0d; + if (endX < startX) + { + (endX, startX) = (startX, endX); + } - switch (currentRun) + //Lines that only contain a linebreak need to be covered here + if(characterLength == 0) { - case ShapedTextCharacters shapedRun: - { - endOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit( - shapedRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(firstTextSourceCharacterIndex + textLength) : - new CharacterHit(firstTextSourceCharacterIndex)); + characterLength = NewLineLength; + } - endX += endOffset; + var runwidth = endX - startX; + var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runwidth, Height), currentPosition, characterLength, currentRun); - var startOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit( - shapedRun.ShapedBuffer.IsLeftToRight ? - new CharacterHit(firstTextSourceCharacterIndex) : - new CharacterHit(firstTextSourceCharacterIndex + textLength)); + if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + { + currentRect = currentRect.WithWidth(currentWidth + runwidth); - startX += startOffset; + var textBounds = result[result.Count - 1]; - var characterHit = shapedRun.GlyphRun.IsLeftToRight ? - shapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) : - shapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); + textBounds.Rectangle = currentRect; - currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; + textBounds.TextRunBounds.Add(currentRunBounds); + } + else + { + currentRect = currentRunBounds.Rectangle; - currentDirection = shapedRun.ShapedBuffer.IsLeftToRight ? - FlowDirection.LeftToRight : - FlowDirection.RightToLeft; + result.Add(new TextBounds(currentRect, currentDirection, new List { currentRunBounds })); + } - if (nextRun is ShapedTextCharacters nextShaped) - { - if (shapedRun.ShapedBuffer.IsLeftToRight == nextShaped.ShapedBuffer.IsLeftToRight) - { - endOffset = nextShaped.GlyphRun.GetDistanceFromCharacterHit( - nextShaped.ShapedBuffer.IsLeftToRight ? - new CharacterHit(firstTextSourceCharacterIndex + textLength) : - new CharacterHit(firstTextSourceCharacterIndex)); + currentWidth += runwidth; + currentPosition += characterLength; - index++; + if (currentDirection == FlowDirection.LeftToRight) + { + if (currentPosition > characterIndex) + { + break; + } + } + else + { + if (currentPosition <= firstTextSourceIndex) + { + break; + } + } - endX += endOffset; + startX = endX; + lastDirection = currentDirection; + remainingLength -= characterLength; - currentRun = nextShaped; + if (remainingLength <= 0) + { + break; + } + } - if (nextShaped.ShapedBuffer.IsLeftToRight) - { - characterHit = nextShaped.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); + return result; + } - currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - } - } - } + private IReadOnlyList GetTextBoundsRightToLeft(int firstTextSourceIndex, int textLength) + { + var characterIndex = firstTextSourceIndex + textLength; - break; - } - default: - { - if (currentPosition + currentRun.TextSourceLength <= firstTextSourceCharacterIndex + textLength) - { - endX += currentRun.Size.Width; - } + var result = new List(TextRuns.Count); + var lastDirection = FlowDirection.LeftToRight; + var currentDirection = lastDirection; - if (currentPosition < firstTextSourceCharacterIndex) - { - startX += currentRun.Size.Width; - } + var currentPosition = FirstTextSourceIndex; + var remainingLength = textLength; - currentPosition += currentRun.TextSourceLength; + var startX = Start + WidthIncludingTrailingWhitespace; + double currentWidth = 0; + var currentRect = Rect.Empty; - break; - } + for (var index = TextRuns.Count - 1; index >= 0; index--) + { + if (TextRuns[index] is not DrawableTextRun currentRun) + { + continue; } - if (endX < startX) + if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex) { - (endX, startX) = (startX, endX); + startX -= currentRun.Size.Width; + + currentPosition += currentRun.TextSourceLength; + + continue; } - var width = endX - startX; + var characterLength = 0; + var endX = startX; - if (!MathUtilities.IsZero(width)) + if (currentRun is ShapedTextCharacters currentShapedRun) { - if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) - { - currentRect = currentRect.WithWidth(currentRect.Width + width); + var offset = Math.Max(0, firstTextSourceIndex - currentPosition); + + currentPosition += offset; - var textBounds = new TextBounds(currentRect, currentDirection); + var startIndex = currentRun.Text.Start + offset; - result[result.Count - 1] = textBounds; + var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( + currentShapedRun.ShapedBuffer.IsLeftToRight ? + new CharacterHit(startIndex + remainingLength) : + new CharacterHit(startIndex)); + + endX += endOffset - currentShapedRun.Size.Width; + + var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit( + currentShapedRun.ShapedBuffer.IsLeftToRight ? + new CharacterHit(startIndex) : + new CharacterHit(startIndex + remainingLength)); + + startX += startOffset - currentShapedRun.Size.Width; + + var endHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _); + var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _); + + characterLength = Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength - endHit.FirstCharacterIndex - endHit.TrailingLength); + + currentDirection = currentShapedRun.ShapedBuffer.IsLeftToRight ? + FlowDirection.LeftToRight : + FlowDirection.RightToLeft; + } + else + { + if (currentPosition + currentRun.TextSourceLength <= characterIndex) + { + endX -= currentRun.Size.Width; } - else + + if (currentPosition < firstTextSourceIndex) { + startX -= currentRun.Size.Width; - currentRect = new Rect(startX, 0, width, Height); + characterLength = currentRun.TextSourceLength; + } + } - result.Add(new TextBounds(currentRect, currentDirection)); + if (endX < startX) + { + (endX, startX) = (startX, endX); + } - } + //Lines that only contain a linebreak need to be covered here + if (characterLength == 0) + { + characterLength = NewLineLength; } + var runWidth = endX - startX; + var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun); + + if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + { + currentRect = currentRect.WithWidth(currentWidth + runWidth); + + var textBounds = result[result.Count - 1]; + + textBounds.Rectangle = currentRect; + + textBounds.TextRunBounds.Add(currentRunBounds); + } + else + { + currentRect = currentRunBounds.Rectangle; + + result.Add(new TextBounds(currentRect, currentDirection, new List { currentRunBounds })); + } + + currentWidth += runWidth; + currentPosition += characterLength; + if (currentDirection == FlowDirection.LeftToRight) { - if (currentPosition > firstTextSourceCharacterIndex + textLength) + if (currentPosition > characterIndex) { break; } } else { - if (currentPosition <= firstTextSourceCharacterIndex) + if (currentPosition <= firstTextSourceIndex) { break; } - - endX += currentRun.Size.Width - endOffset; } lastDirection = currentDirection; - startX = endX; + remainingLength -= characterLength; + + if (remainingLength <= 0) + { + break; + } } return result; } + public override IReadOnlyList GetTextBounds(int firstTextSourceIndex, int textLength) + { + if (_paragraphProperties.FlowDirection == FlowDirection.LeftToRight) + { + return GetTextBoundsLeftToRight(firstTextSourceIndex, textLength); + } + + return GetTextBoundsRightToLeft(firstTextSourceIndex, textLength); + } + public TextLineImpl FinalizeLine() { _textLineMetrics = CreateLineMetrics(); @@ -629,7 +742,7 @@ namespace Avalonia.Media.TextFormatting // Build up the collection of ordered runs. var run = _textRuns[0]; - OrderedBidiRun orderedRun = new(run, GetRunBidiLevel(run, _flowDirection)); + OrderedBidiRun orderedRun = new(run, GetRunBidiLevel(run, _resolvedFlowDirection)); var current = orderedRun; @@ -637,7 +750,7 @@ namespace Avalonia.Media.TextFormatting { run = _textRuns[i]; - current.Next = new OrderedBidiRun(run, GetRunBidiLevel(run, _flowDirection)); + current.Next = new OrderedBidiRun(run, GetRunBidiLevel(run, _resolvedFlowDirection)); current = current.Next; } @@ -656,7 +769,7 @@ namespace Avalonia.Media.TextFormatting { var currentRun = _textRuns[i]; - var level = GetRunBidiLevel(currentRun, _flowDirection); + var level = GetRunBidiLevel(currentRun, _resolvedFlowDirection); if (level > max) { @@ -1136,8 +1249,7 @@ namespace Avalonia.Media.TextFormatting } } - var start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth, - _paragraphProperties.TextAlignment, _paragraphProperties.FlowDirection); + var start = GetParagraphOffsetX(width, widthIncludingWhitespace); if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight)) { @@ -1151,6 +1263,55 @@ namespace Avalonia.Media.TextFormatting -ascent, trailingWhitespaceLength, width, widthIncludingWhitespace); } + /// + /// Gets the text line offset x. + /// + /// The line width. + /// The paragraph width including whitespace. + + /// The paragraph offset. + private double GetParagraphOffsetX(double width, double widthIncludingTrailingWhitespace) + { + if (double.IsPositiveInfinity(_paragraphWidth)) + { + return 0; + } + + var textAlignment = _paragraphProperties.TextAlignment; + var paragraphFlowDirection = _paragraphProperties.FlowDirection; + + switch (textAlignment) + { + case TextAlignment.Start: + { + textAlignment = paragraphFlowDirection == FlowDirection.LeftToRight ? TextAlignment.Left : TextAlignment.Right; + break; + } + case TextAlignment.End: + { + textAlignment = paragraphFlowDirection == FlowDirection.RightToLeft ? TextAlignment.Left : TextAlignment.Right; + break; + } + case TextAlignment.DetectFromContent: + { + textAlignment = _resolvedFlowDirection == FlowDirection.LeftToRight ? TextAlignment.Left : TextAlignment.Right; + break; + } + } + + switch (textAlignment) + { + case TextAlignment.Center: + return Math.Max(0, (_paragraphWidth - width) / 2); + + case TextAlignment.Right: + return Math.Max(0, _paragraphWidth - widthIncludingTrailingWhitespace); + + default: + return 0; + } + } + private sealed class OrderedBidiRun { public OrderedBidiRun(DrawableTextRun run, sbyte level) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextRunBounds.cs b/src/Avalonia.Base/Media/TextFormatting/TextRunBounds.cs new file mode 100644 index 0000000000..91150160ed --- /dev/null +++ b/src/Avalonia.Base/Media/TextFormatting/TextRunBounds.cs @@ -0,0 +1,39 @@ +namespace Avalonia.Media.TextFormatting +{ + /// + /// The bounding rectangle of text run + /// + public sealed class TextRunBounds + { + /// + /// Constructing TextRunBounds + /// + internal TextRunBounds(Rect bounds, int firstCharacterIndex, int length, TextRun textRun) + { + Rectangle = bounds; + TextSourceCharacterIndex = firstCharacterIndex; + Length = length; + TextRun = textRun; + } + + /// + /// First text source character index of text run + /// + public int TextSourceCharacterIndex { get; } + + /// + /// character length of bounded text run + /// + public int Length { get; } + + /// + /// Text run bounding rectangle + /// + public Rect Rectangle { get; } + + /// + /// text run + /// + public TextRun TextRun { get; } + } +} diff --git a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs index a7fe92dc9a..4e75bb921e 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs @@ -16,7 +16,7 @@ namespace Avalonia.Media.TextFormatting { Typeface = typeface; FontRenderingEmSize = fontRenderingEmSize; - BidLevel = bidiLevel; + BidiLevel = bidiLevel; Culture = culture; IncrementalTabWidth = incrementalTabWidth; } @@ -33,7 +33,7 @@ namespace Avalonia.Media.TextFormatting /// /// Get the bidi level of the text. /// - public sbyte BidLevel { get; } + public sbyte BidiLevel { get; } /// /// Get the culture. diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs new file mode 100644 index 0000000000..7ce5453ec4 --- /dev/null +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDi.trie.cs @@ -0,0 +1,42 @@ +using System; + +namespace Avalonia.Media.TextFormatting.Unicode +{ + internal static class BiDiTrie + { + public static ReadOnlySpan Data => new byte[] + { + 0,16,0,0,0,0,0,0,0,0,174,224,0,68,12,187,243,236,157,11,176,85,85,25,199,151,115,207,189,247,156,203,227,94,244,132,151,128,58,122,53,184,10,74,112,26,206,61,64,105,130,89,22,163,121,85,12,17,38,26,7,197,72,37,152,166,59,56,142,132,236,64,144,16,196,212,6,26,167,98,34,197,158,71,83,10,134,116,116,236,161,165,4,50,38,61,40,64,137,178, + 28,109,6,164,255,102,175,205,93,44,246,99,61,247,62,192,254,102,126,243,173,189,246,218,235,251,214,183,190,253,60,251,156,51,171,129,144,91,193,87,192,157,224,1,176,14,172,7,143,3,7,172,144,208,63,3,191,0,207,130,223,130,109,224,79,224,85,240,23,176,15,188,5,246,131,183,3,182,63,4,26,115,225,253,247,193,186,51,192,96,208,1,206,7,99,64,23,184,24,92, + 10,38,131,43,193,117,96,6,152,5,230,128,249,96,30,88,0,22,129,101,224,223,121,66,222,5,7,65,174,64,200,80,212,141,6,99,193,90,44,175,134,94,227,150,115,132,124,15,108,4,143,131,39,192,102,240,28,93,126,17,188,194,44,191,6,118,211,246,251,193,219,116,251,67,57,111,44,141,141,132,20,192,0,112,6,24,12,58,26,123,251,63,191,209,107,127,24,109,47,108, + 244,236,249,246,29,212,141,109,244,180,203,4,148,39,49,203,159,68,249,74,186,124,53,244,245,180,252,5,232,91,192,92,112,144,246,227,128,30,44,47,4,75,193,125,76,63,142,65,30,162,253,62,194,245,191,193,176,189,31,115,253,61,133,229,167,192,211,24,239,211,116,204,91,176,252,124,163,231,203,75,92,251,237,88,126,148,137,141,3,118,161,110,15,215,110,31,150,107,200,143, + 183,104,253,255,160,79,107,242,202,5,232,211,105,253,155,232,107,64,147,55,239,131,154,188,249,60,139,182,27,78,245,72,170,29,134,49,168,27,23,80,239,80,182,129,223,180,168,197,232,227,232,247,83,96,50,184,6,244,96,28,27,193,13,40,223,8,22,162,188,24,204,199,24,230,96,121,62,184,131,250,114,119,147,55,110,159,217,76,185,22,192,38,112,15,221,118,37,244,55,185, + 237,107,224,48,234,182,210,242,90,102,204,223,13,104,91,19,96,67,200,118,47,48,229,223,131,29,133,248,88,241,125,252,153,234,61,224,128,164,95,239,128,67,92,157,19,149,203,77,92,46,55,29,159,3,110,31,141,200,131,45,180,175,124,75,111,223,173,76,185,198,113,38,214,13,5,91,209,231,11,76,191,127,104,234,221,39,55,198,196,229,231,104,187,137,46,239,68,249,175,180, + 159,55,160,255,195,249,234,128,119,154,142,221,127,30,51,184,223,59,25,36,139,1,57,101,98,80,19,56,126,56,25,36,139,1,201,98,208,144,197,32,139,1,201,98,208,144,197,32,139,1,201,98,208,144,197,160,94,99,176,179,201,187,174,117,159,9,56,2,237,115,205,189,229,66,179,247,44,205,95,190,150,62,163,217,203,212,13,96,218,59,96,16,150,75,180,110,24,179,110,36, + 182,185,144,46,143,133,254,40,45,175,198,115,156,75,104,249,49,250,156,232,114,44,95,213,236,61,183,115,151,175,109,246,158,211,77,231,108,29,224,158,33,221,200,173,119,50,72,22,3,146,197,160,33,139,65,22,3,146,197,160,33,139,65,22,3,146,197,160,225,196,141,193,205,184,198,107,193,53,104,255,130,247,185,178,10,231,182,28,95,247,29,212,141,4,31,1,75,209,247,189, + 96,46,103,227,81,172,187,28,117,183,195,135,175,130,187,184,235,205,53,117,240,252,184,212,223,99,6,88,71,203,165,0,182,131,246,86,66,166,128,89,96,61,248,73,107,239,250,255,50,229,18,24,209,134,207,186,193,15,192,223,64,251,0,66,38,130,59,193,19,224,101,80,60,29,109,17,163,85,125,97,31,122,45,88,119,90,60,175,11,182,251,96,159,222,114,55,202,171,192,203, + 224,69,216,43,130,182,126,98,253,216,162,27,62,172,203,236,147,180,98,208,157,197,159,100,249,71,82,139,65,119,150,127,36,237,252,187,2,231,128,25,253,78,221,249,143,187,62,88,210,124,236,231,221,43,155,123,223,79,89,207,189,167,229,112,92,197,93,223,60,88,135,207,219,54,98,12,223,134,95,235,221,103,134,205,222,251,97,63,106,238,93,239,62,111,124,146,46,255,146,174, + 255,21,244,175,153,54,47,49,101,135,121,87,104,71,115,239,251,131,238,251,104,187,2,218,57,13,158,253,61,204,186,127,161,252,110,72,91,7,28,196,186,92,222,43,187,244,205,123,253,223,79,183,41,230,143,125,239,111,8,179,236,112,156,21,177,206,1,195,177,126,20,215,102,12,181,55,14,250,99,224,19,116,253,228,152,190,156,0,174,102,182,153,134,242,76,174,143,155,176,124, + 27,173,155,39,209,255,135,35,222,107,236,65,63,11,21,124,117,100,200,123,122,57,213,78,138,108,166,121,177,138,241,229,225,58,240,203,225,120,30,251,218,79,21,223,169,116,20,120,4,49,216,0,126,200,196,162,70,143,115,19,66,252,168,209,245,155,19,190,119,219,72,245,34,58,151,223,247,215,53,123,250,73,140,97,170,1,159,182,129,45,121,79,63,7,253,59,240,74,130,115, + 226,104,242,26,124,221,29,227,239,114,197,220,255,99,29,238,51,14,71,13,57,240,25,238,29,212,207,130,107,90,188,119,78,95,207,247,230,206,110,58,158,197,52,135,28,142,21,1,159,1,214,20,222,7,174,25,96,106,139,119,237,193,214,181,50,227,156,137,242,46,193,207,44,157,147,148,189,121,239,251,19,81,12,44,68,175,255,0,214,159,29,210,230,60,90,63,34,166,143,161, + 17,140,118,191,71,81,16,107,91,166,237,198,115,237,251,23,188,239,144,248,223,23,25,26,192,69,220,186,75,4,109,14,213,160,127,193,27,223,104,166,110,52,37,23,98,127,116,4,147,90,226,109,250,251,250,108,170,167,67,207,101,142,127,53,212,213,40,61,45,225,239,232,187,251,210,93,96,9,88,1,86,211,62,106,76,155,26,248,22,237,99,43,152,89,72,63,223,55,193,159, + 77,17,108,67,187,109,33,60,19,179,237,166,83,0,66,240,112,54,150,110,128,155,197,35,122,10,45,183,41,210,151,161,155,246,87,161,148,24,42,71,232,36,85,50,156,116,209,229,33,160,200,232,34,104,23,164,200,216,169,112,36,35,211,48,22,2,123,83,143,142,167,66,73,70,22,80,251,61,71,237,183,37,99,184,174,165,45,128,190,33,245,109,22,40,6,228,126,137,214,85, + 24,92,241,203,238,118,126,217,109,235,226,231,57,223,214,197,173,231,235,216,126,109,137,237,254,235,221,126,82,34,50,151,65,115,47,130,169,237,101,125,209,177,201,82,62,197,9,139,125,156,196,205,93,84,155,147,81,84,198,42,51,47,39,155,4,29,59,220,243,212,196,58,216,39,202,10,76,228,116,57,164,60,241,36,39,31,66,133,146,163,243,92,162,229,34,213,21,74,89,130, + 92,157,83,182,64,94,144,18,83,206,113,62,229,50,8,31,131,184,152,71,229,38,223,71,80,91,191,141,232,253,168,201,57,74,115,206,77,230,254,201,156,183,73,197,33,237,243,131,12,34,177,240,219,250,231,15,31,190,159,160,62,211,20,223,126,144,31,113,99,14,235,75,100,123,25,255,210,138,81,154,115,35,50,31,42,125,232,138,238,124,184,215,3,46,113,109,88,123,166,108, + 235,74,84,222,7,249,25,180,63,216,244,69,182,255,176,253,82,116,255,54,33,58,115,26,151,71,108,255,170,199,31,83,194,231,135,175,77,238,207,42,99,59,145,142,173,182,124,229,207,217,62,110,126,185,90,196,151,180,207,227,190,196,229,83,216,190,46,146,59,170,249,101,35,54,97,115,22,54,95,113,146,246,117,134,170,109,209,57,139,186,206,83,149,176,115,94,92,78,197,29, + 183,101,175,59,85,69,212,127,157,188,79,235,188,147,132,61,62,167,202,100,92,107,149,84,91,187,200,4,232,241,208,230,237,149,21,8,219,150,173,15,18,19,54,146,68,87,100,198,196,174,227,183,79,66,194,108,217,142,135,74,31,124,127,34,109,117,109,165,33,38,99,31,117,126,23,57,215,171,94,7,152,18,247,125,161,100,228,225,129,85,242,208,64,211,199,90,95,76,206,169, + 136,29,222,166,236,62,24,180,191,233,30,59,253,235,21,182,157,104,126,242,207,224,221,119,98,76,73,26,199,219,168,99,174,104,63,42,54,248,253,89,231,90,223,164,36,225,71,208,121,214,166,45,214,102,212,185,159,247,69,117,31,147,221,71,109,156,219,147,190,214,10,242,39,204,47,118,189,106,223,182,198,19,53,71,65,99,50,45,188,141,168,117,54,99,200,183,211,149,160,249, + 87,201,81,153,57,143,219,71,131,252,75,83,252,227,174,234,179,31,221,103,70,166,182,119,197,189,94,20,133,125,47,215,149,160,207,29,101,9,123,239,119,4,248,52,56,7,92,20,209,174,157,27,91,123,8,67,40,21,114,71,169,74,22,148,252,235,214,176,246,225,219,47,199,246,203,142,110,175,43,236,92,4,221,99,200,206,179,104,110,70,221,215,132,105,86,68,62,3,9,178, + 39,226,135,136,191,81,219,203,32,227,187,168,255,114,156,215,81,37,157,29,252,119,30,76,248,109,90,244,199,26,157,115,21,1,218,13,115,34,141,93,180,63,81,223,121,17,189,94,145,181,159,148,184,126,179,223,151,58,222,167,194,168,42,201,143,234,34,125,160,91,160,251,65,247,133,110,133,238,15,61,0,186,13,154,208,109,223,135,229,34,150,207,132,30,8,61,8,186,29,122, + 48,244,251,153,118,68,58,6,101,14,95,226,230,48,106,93,82,241,247,125,182,97,211,70,254,219,20,27,182,147,28,127,146,241,211,57,86,37,57,183,182,108,242,251,188,219,63,95,23,134,168,63,73,30,3,162,150,195,234,76,217,246,251,230,115,68,214,166,200,123,97,58,34,155,195,162,249,16,135,138,68,109,103,195,158,136,63,54,199,43,98,63,204,23,190,94,182,223,168,49, + 152,176,225,111,103,162,141,138,77,217,88,233,140,209,244,56,121,159,117,250,178,33,236,59,221,201,157,23,39,28,246,190,31,63,238,112,58,223,207,159,70,237,79,165,246,103,96,121,58,45,207,68,249,243,199,248,149,164,164,121,223,163,123,141,88,175,215,216,73,29,227,69,236,154,56,231,156,8,247,53,186,231,184,176,24,165,113,238,246,237,218,236,219,212,184,162,226,110,82,162, + 108,196,249,28,118,30,76,99,94,227,108,199,181,211,181,231,47,219,20,27,253,71,197,43,106,126,249,114,26,34,58,135,105,251,108,211,166,232,254,166,154,163,65,219,201,230,76,84,223,34,231,5,155,34,122,204,78,43,215,69,246,199,164,37,42,199,194,252,17,201,207,36,198,34,234,135,168,63,162,62,203,216,21,21,145,220,77,34,151,69,237,217,222,175,85,226,33,114,92,11, + 138,91,82,49,228,219,132,249,97,226,120,172,42,113,190,218,144,184,123,35,255,25,174,143,238,189,150,201,251,45,219,251,130,72,174,155,234,91,71,252,152,166,113,14,243,69,118,44,113,191,77,104,234,88,99,67,146,176,35,179,159,152,122,95,67,245,184,47,115,110,240,197,84,63,65,253,138,74,146,57,162,98,203,100,158,185,199,110,153,92,176,121,220,78,251,51,104,94,252,99, + 78,82,191,53,19,246,251,51,124,125,84,187,52,126,39,135,95,167,251,187,68,38,252,115,223,27,181,53,246,122,24,159,42,238,254,20,149,79,162,121,154,86,110,197,249,152,100,28,195,16,253,189,60,27,168,198,218,214,252,37,145,59,174,4,221,151,120,117,5,124,78,153,63,250,57,229,16,250,251,143,67,168,46,74,156,251,138,33,243,93,150,232,163,221,0,188,152,232,211,164, + 63,178,162,107,199,196,189,167,159,27,58,247,178,238,246,46,252,181,11,155,151,65,215,51,186,231,147,56,191,252,255,71,24,14,46,3,227,193,40,112,5,205,233,82,0,21,9,74,41,83,214,164,162,73,145,209,174,176,117,30,159,123,175,74,174,123,175,139,92,15,61,21,250,6,232,105,208,36,52,230,37,138,223,95,24,174,136,252,166,127,208,182,252,61,181,123,254,208,141,165, + 10,113,191,75,25,151,255,162,231,70,209,253,167,72,46,45,85,201,164,35,223,55,170,88,102,74,2,54,42,145,20,58,112,126,196,119,97,250,64,183,72,127,39,166,34,137,13,137,182,121,211,176,42,153,53,172,139,204,134,190,25,250,139,208,183,64,127,9,122,14,244,109,208,183,66,207,133,190,29,122,30,244,151,161,137,196,184,158,197,54,207,72,110,67,52,249,7,108,254,29, + 54,247,66,239,129,126,3,122,31,244,126,232,55,161,15,64,255,211,176,79,95,235,172,146,133,157,93,228,110,232,69,208,14,244,98,232,37,208,95,135,190,7,122,41,244,10,232,123,161,151,67,47,131,94,9,253,13,232,85,208,247,65,223,15,189,26,250,1,232,53,157,102,125,140,99,7,108,110,135,205,157,208,175,38,108,155,128,67,176,123,208,170,93,95,108,143,69,206,70,199, + 5,85,114,246,5,93,228,92,232,115,160,135,65,127,8,186,19,122,56,52,73,120,30,236,197,72,86,108,231,129,174,232,218,77,235,247,173,125,127,116,255,15,73,213,182,202,239,108,235,92,159,134,137,191,158,239,51,41,49,53,143,182,238,143,226,226,104,99,159,172,199,227,150,234,113,36,109,31,146,242,223,244,248,249,237,85,37,169,248,155,154,195,184,216,198,197,92,103,174,77, + 140,67,39,31,85,164,30,198,171,147,7,186,199,255,52,206,223,166,8,242,39,105,177,241,31,145,50,232,94,255,164,109,95,20,87,100,234,101,250,213,17,21,159,85,125,20,181,21,214,214,190,252,31,0,0,255,255,0,0,0,255,255,99,102,0,0}; + } +} diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs new file mode 100644 index 0000000000..80158e9d7e --- /dev/null +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreak.trie.cs @@ -0,0 +1,31 @@ +using System; + +namespace Avalonia.Media.TextFormatting.Unicode +{ + internal static class GraphemeBreakTrie + { + public static ReadOnlySpan Data => new byte[] + { + 0,14,16,0,0,0,0,0,0,0,133,64,0,245,7,10,248,236,157,127,136,29,87,21,199,103,157,111,246,157,100,211,138,180,69,177,5,241,7,88,91,40,182,150,210,12,138,161,88,27,163,96,241,15,75,161,210,18,44,149,10,193,46,24,80,49,254,163,33,16,136,54,127,84,8,34,18,253,199,68,196,42,136,154,82,21,149,6,218,82,43,72,107,161,54,22,196,173,10,110, + 16,140,210,82,250,125,190,51,228,228,228,206,204,157,121,119,230,109,182,243,133,15,231,220,115,207,156,123,239,121,111,247,253,216,129,189,33,207,178,130,220,66,118,147,61,228,126,19,43,230,180,171,228,139,100,31,217,31,145,127,128,28,174,153,63,66,142,146,99,228,56,249,49,249,5,249,21,57,101,242,158,36,127,36,207,145,23,201,26,89,39,255,34,103,201,43,4,200,178,47, + 147,175,146,131,152,93,91,242,40,217,206,216,55,200,17,114,148,28,35,199,201,9,242,19,114,146,252,134,252,150,60,65,158,54,227,63,145,191,104,254,75,228,140,94,255,63,242,32,89,218,50,91,103,43,237,25,218,55,109,57,87,255,205,244,223,166,227,119,211,190,135,92,110,214,47,152,127,61,99,103,39,51,127,7,253,157,90,175,32,31,162,255,49,29,223,78,123,135,250,119, + 211,222,71,246,146,255,154,243,238,227,120,63,57,64,14,155,58,5,185,82,206,241,46,229,26,242,126,165,48,185,69,13,95,143,204,43,18,243,16,207,243,16,121,158,231,125,94,207,252,45,142,191,87,246,7,231,231,95,193,248,247,93,15,30,118,227,194,240,51,157,251,37,237,99,234,239,37,47,235,252,83,140,253,129,60,71,78,147,53,178,174,121,103,213,190,26,168,143,101,62, + 255,150,47,140,151,172,146,203,107,230,139,72,62,201,243,223,165,61,184,201,196,247,146,7,106,206,93,140,100,125,247,224,42,247,248,158,12,60,7,138,4,252,213,252,12,188,61,193,115,170,24,201,198,30,100,99,15,242,177,7,99,15,178,177,7,249,216,131,177,7,217,216,131,124,236,193,216,131,108,236,65,62,246,96,236,65,150,188,7,87,47,207,190,75,43,199,159,136,248,254, + 226,189,188,230,102,243,153,247,24,191,83,123,133,236,100,108,151,198,111,167,189,131,92,37,179,239,3,239,162,15,250,159,214,249,251,105,87,201,62,29,239,167,253,51,237,129,134,207,210,135,57,127,43,235,124,152,220,70,118,145,143,144,221,228,163,228,214,113,46,27,251,146,141,207,9,25,127,30,198,223,5,89,178,223,5,55,144,29,228,3,164,24,223,139,100,99,15,178,177,7, + 249,216,131,62,122,112,164,231,191,167,236,87,138,0,187,3,177,51,198,191,199,253,237,179,88,0,187,46,153,113,144,172,109,111,230,20,243,158,13,228,190,229,210,243,199,187,56,62,72,78,145,236,141,179,216,141,180,159,33,223,33,143,144,117,178,115,37,203,62,71,94,220,202,191,125,109,203,50,89,154,113,167,241,197,113,98,229,194,88,136,47,153,26,63,162,255,15,94,119,45, + 247,113,47,57,176,61,174,198,72,54,246,96,105,236,193,216,131,108,236,193,210,230,235,193,58,95,23,50,190,46,60,202,247,9,167,249,122,248,146,222,231,177,198,239,174,222,202,215,231,119,232,107,244,223,201,239,152,179,194,207,44,143,47,207,230,127,79,123,141,121,13,127,54,240,94,227,36,57,189,124,238,62,178,53,250,235,21,239,73,254,195,248,171,102,110,153,223,131,93,170, + 247,152,21,1,46,227,220,149,102,254,157,147,115,247,61,77,185,118,114,254,253,95,239,171,169,117,115,205,92,65,174,227,57,111,114,239,87,62,200,107,110,35,31,159,94,11,222,203,164,53,110,113,121,69,4,159,50,235,223,75,255,179,110,63,15,112,188,91,235,126,161,97,175,133,225,43,45,114,139,30,185,175,67,79,138,158,249,46,123,179,99,192,245,246,178,7,39,184,230,195, + 129,199,228,231,27,228,113,42,90,178,71,239,3,91,85,246,56,86,107,248,60,46,204,127,189,81,204,193,209,134,207,118,47,187,241,177,13,120,111,221,113,238,233,135,21,251,186,34,209,253,159,63,104,121,238,159,6,242,207,246,220,135,175,233,207,255,161,13,240,123,224,65,238,225,50,190,206,127,211,236,229,219,9,246,181,74,126,61,153,217,83,180,79,145,103,54,192,121,139,72, + 94,224,94,255,214,176,223,67,29,207,243,207,139,160,15,135,220,30,255,125,17,236,185,232,192,35,27,96,15,215,231,75,255,39,231,219,243,60,130,55,24,127,41,242,154,60,130,81,139,211,216,255,44,217,243,56,239,200,162,31,55,188,206,73,41,212,176,153,133,139,144,114,223,165,197,212,9,248,155,93,147,10,98,133,22,164,254,221,149,90,232,129,20,123,26,66,168,97,210,144, + 227,107,100,53,57,41,53,81,139,148,69,91,10,9,217,204,66,11,46,86,33,130,216,58,214,135,62,215,177,0,36,16,203,52,46,138,31,195,252,108,214,9,117,147,145,18,3,28,226,226,165,38,165,211,81,147,142,215,133,36,166,230,36,148,16,56,87,121,29,166,206,156,130,214,18,199,84,162,216,92,104,12,186,95,49,22,101,226,130,132,10,68,247,8,71,72,147,10,208,1, + 49,126,149,80,193,68,237,144,130,33,239,161,126,174,181,183,168,159,39,170,153,87,144,66,121,75,134,88,175,73,112,207,61,184,121,113,115,80,242,6,144,128,124,78,250,18,28,161,24,166,193,134,49,76,124,40,193,80,55,39,126,114,64,65,215,135,142,197,128,6,36,144,47,74,89,27,21,196,10,202,34,4,119,158,161,215,246,123,128,163,148,40,126,140,50,208,32,52,204,251, + 92,216,64,207,130,243,225,206,58,148,224,122,43,26,67,69,46,2,99,84,212,76,181,63,4,98,177,18,205,135,99,42,40,50,29,168,21,141,165,22,148,190,132,10,134,22,140,133,233,39,12,165,68,241,99,148,129,10,193,16,43,196,38,38,22,2,231,204,76,76,166,3,55,6,137,17,34,115,196,128,152,194,9,5,183,190,152,56,20,63,198,52,80,35,40,139,16,212,138,250, + 18,240,203,60,81,166,18,5,74,157,160,196,8,138,40,139,20,148,210,151,169,19,41,84,196,68,129,193,230,75,13,208,156,121,5,181,162,62,42,136,169,131,26,82,9,145,251,133,201,109,83,27,1,134,18,148,190,215,8,89,43,81,82,158,9,45,145,138,184,173,25,18,90,208,54,31,61,48,175,16,217,55,184,124,113,185,125,74,26,214,66,205,92,91,193,209,69,80,43,234, + 35,34,183,171,208,245,194,134,154,80,250,20,20,49,62,2,72,3,112,62,34,137,221,99,95,130,65,28,8,196,189,68,129,241,197,1,67,159,130,34,10,122,90,71,204,58,168,161,220,147,181,165,143,26,164,2,68,82,10,1,164,156,28,72,168,65,140,21,7,140,191,40,33,130,174,53,165,237,133,9,37,129,115,136,139,139,155,79,45,184,53,196,0,55,7,51,215,86,210,0, + 26,16,151,55,175,96,240,99,68,34,13,243,94,240,129,138,57,209,49,76,204,11,3,81,181,214,60,130,97,72,65,73,85,7,74,40,134,10,188,208,48,174,138,165,148,40,80,36,144,131,158,214,20,183,246,80,66,34,186,174,93,55,135,26,250,16,90,32,74,234,245,69,129,67,92,92,148,170,58,67,10,21,136,130,138,113,151,117,98,114,164,41,169,71,161,167,122,48,216,57, + 148,3,39,40,109,36,230,58,81,234,242,218,74,20,84,248,153,137,45,90,216,228,107,35,64,76,174,29,135,4,71,223,130,33,52,215,70,80,82,74,180,166,164,44,218,81,8,32,21,113,24,250,18,18,215,19,3,34,144,132,107,136,137,137,137,87,9,106,197,128,6,250,16,220,30,196,204,73,197,88,90,212,142,149,40,246,90,84,96,133,0,125,75,12,104,64,2,121,162,182, + 79,33,97,29,81,80,129,4,242,36,48,143,68,123,234,42,232,94,68,125,24,74,137,3,138,24,127,72,65,73,37,49,160,1,49,121,162,182,141,96,174,19,227,35,128,168,109,146,40,112,72,32,134,166,98,29,36,6,68,32,198,79,37,49,192,81,10,138,56,80,38,168,68,237,144,66,36,67,236,35,54,15,1,68,129,97,30,137,171,5,199,84,168,64,212,166,18,58,146,153, + 189,160,2,169,240,83,9,21,248,156,210,98,234,24,27,35,9,80,214,192,212,49,113,24,155,82,80,68,73,89,19,134,148,66,13,41,106,103,166,31,210,129,58,193,209,69,104,153,47,202,34,5,197,250,152,14,84,168,161,47,193,209,103,253,33,133,30,73,181,86,147,208,148,208,49,183,173,16,160,42,142,72,230,21,106,104,154,71,36,165,80,131,157,207,106,114,82,11,142,212, + 245,96,232,83,162,244,85,87,90,176,104,137,50,228,90,178,32,182,110,0,172,182,45,152,149,158,185,196,209,86,219,18,226,181,178,1,240,226,191,181,184,160,103,125,50,244,122,94,175,1,0,0,255,255,0,0,0,255,255,99,102,0,0}; + } +} diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs index 471cb52bea..ff39dc5011 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System.IO; +using System.Runtime.CompilerServices; namespace Avalonia.Media.TextFormatting.Unicode { @@ -17,14 +18,14 @@ namespace Avalonia.Media.TextFormatting.Unicode internal const int SCRIPT_SHIFT = CATEGORY_BITS; internal const int LINEBREAK_SHIFT = CATEGORY_BITS + SCRIPT_BITS; - + internal const int BIDIPAIREDBRACKEDTYPE_SHIFT = BIDIPAIREDBRACKED_BITS; internal const int BIDICLASS_SHIFT = BIDIPAIREDBRACKED_BITS + BIDIPAIREDBRACKEDTYPE_BITS; - + internal const int CATEGORY_MASK = (1 << CATEGORY_BITS) - 1; internal const int SCRIPT_MASK = (1 << SCRIPT_BITS) - 1; internal const int LINEBREAK_MASK = (1 << LINEBREAK_BITS) - 1; - + internal const int BIDIPAIREDBRACKED_MASK = (1 << BIDIPAIREDBRACKED_BITS) - 1; internal const int BIDIPAIREDBRACKEDTYPE_MASK = (1 << BIDIPAIREDBRACKEDTYPE_BITS) - 1; internal const int BIDICLASS_MASK = (1 << BIDICLASS_BITS) - 1; @@ -35,9 +36,29 @@ namespace Avalonia.Media.TextFormatting.Unicode static UnicodeData() { - s_unicodeDataTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.UnicodeData.trie")!); - s_graphemeBreakTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.GraphemeBreak.trie")!); - s_biDiTrie = new UnicodeTrie(typeof(UnicodeData).Assembly.GetManifestResourceStream("Avalonia.Assets.BiDi.trie")!); + unsafe + { + var unicodeData = UnicodeDataTrie.Data; + + fixed (byte* unicodeDataPtr = unicodeData) + { + s_unicodeDataTrie = new UnicodeTrie(new UnmanagedMemoryStream(unicodeDataPtr, unicodeData.Length)); + } + + var graphemeData = GraphemeBreakTrie.Data; + + fixed (byte* graphemeDataPtr = graphemeData) + { + s_graphemeBreakTrie = new UnicodeTrie(new UnmanagedMemoryStream(graphemeDataPtr, graphemeData.Length)); + } + + var bidiData = BiDiTrie.Data; + + fixed (byte* bidiDataPtr = bidiData) + { + s_biDiTrie = new UnicodeTrie(new UnmanagedMemoryStream(bidiDataPtr, bidiData.Length)); + } + } } /// @@ -72,7 +93,7 @@ namespace Avalonia.Media.TextFormatting.Unicode { return (BidiClass)((s_biDiTrie.Get(codepoint) >> BIDICLASS_SHIFT) & BIDICLASS_MASK); } - + /// /// Gets the for a Unicode codepoint. /// @@ -83,7 +104,7 @@ namespace Avalonia.Media.TextFormatting.Unicode { return (BidiPairedBracketType)((s_biDiTrie.Get(codepoint) >> BIDIPAIREDBRACKEDTYPE_SHIFT) & BIDIPAIREDBRACKEDTYPE_MASK); } - + /// /// Gets the paired bracket for a Unicode codepoint. /// diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.trie.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.trie.cs new file mode 100644 index 0000000000..dd55fda374 --- /dev/null +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.trie.cs @@ -0,0 +1,122 @@ +using System; + +namespace Avalonia.Media.TextFormatting.Unicode +{ + internal static class UnicodeDataTrie + { + public static ReadOnlySpan Data => new byte[] + { + 0,16,0,0,0,0,0,0,0,1,154,144,0,161,43,94,212,236,157,15,148,28,85,157,239,107,230,215,147,204,84,146,78,103,152,252,115,128,16,8,241,224,234,238,193,197,221,163,139,231,117,2,132,158,162,51,52,67,155,63,244,152,233,66,130,202,81,247,225,209,221,167,7,125,13,139,74,202,48,84,134,89,16,199,5,70,81,244,249,111,117,209,93,93,229,5,68,157,41,218, + 177,9,35,168,3,72,16,124,60,149,125,111,87,217,231,62,193,247,190,53,117,59,115,231,166,254,220,234,170,234,10,58,57,231,147,223,253,255,251,221,63,117,235,214,173,59,213,151,100,20,101,47,208,193,91,193,181,224,0,56,4,110,3,147,224,51,224,139,224,31,193,127,7,223,5,223,7,143,130,199,193,51,224,57,240,60,248,13,80,186,20,165,27,228,192,6,112,26,56,19, + 188,10,188,6,188,30,108,7,26,24,2,151,129,125,224,74,240,14,240,87,224,253,224,131,224,58,96,128,155,192,173,224,239,192,39,193,231,193,87,192,55,193,183,193,247,192,15,192,143,193,83,224,25,240,28,120,30,188,8,104,153,162,172,0,39,129,126,112,6,248,35,240,185,94,69,249,83,200,191,0,231,129,29,96,16,148,193,59,215,41,202,217,39,33,30,252,5,184,7,105, + 135,17,126,57,184,202,142,95,166,40,239,5,53,112,3,48,193,71,192,237,224,83,224,11,224,171,224,27,224,1,80,7,179,224,49,240,52,248,5,248,21,248,15,208,177,92,81,122,192,26,176,30,108,2,47,7,127,2,206,131,238,63,135,252,79,224,66,48,8,118,129,125,224,74,112,21,120,231,114,199,246,247,66,94,11,14,48,255,69,200,251,94,212,227,16,252,183,128,219,193, + 39,193,231,89,252,165,136,223,11,116,240,86,112,53,184,100,13,218,22,241,23,160,190,239,129,255,61,140,107,25,31,2,7,24,135,24,223,92,238,200,111,67,78,131,135,152,255,81,38,31,135,124,6,60,7,246,162,252,189,224,121,184,111,67,220,71,99,226,14,240,34,202,188,27,242,110,208,213,173,40,171,192,90,112,10,56,19,124,17,225,95,6,175,130,251,53,224,245,224,235, + 240,127,3,117,189,0,238,251,224,222,217,237,148,119,9,228,94,160,131,183,130,119,128,105,132,255,21,228,53,96,207,6,69,185,30,242,33,132,29,132,28,7,31,3,119,129,207,117,59,237,251,35,196,61,102,143,157,110,232,0,79,195,253,75,240,107,240,0,252,47,64,214,33,103,193,3,176,225,49,200,39,193,179,224,127,131,127,197,245,180,19,225,117,240,6,123,252,245,34,109, + 175,115,93,253,166,219,185,62,121,244,53,139,253,74,15,108,71,88,6,114,37,184,2,121,251,32,79,6,91,192,107,215,43,202,54,48,0,94,9,255,57,160,128,122,157,11,121,62,184,26,121,223,3,174,5,22,198,81,17,97,159,66,218,207,130,47,129,175,129,251,192,52,120,8,252,8,60,9,158,5,207,129,171,193,123,64,21,101,62,15,249,34,200,192,221,3,214,108,112,194, + 47,69,153,21,240,12,202,255,25,120,11,194,222,2,158,227,220,111,7,87,131,247,128,141,96,51,56,11,92,11,14,128,67,224,108,48,6,62,10,62,14,254,27,248,50,211,241,85,91,255,58,69,249,237,58,199,127,45,236,184,1,220,11,247,189,62,60,203,234,242,44,195,100,242,86,112,187,16,247,172,11,85,148,241,90,144,7,111,178,251,162,199,9,107,114,0,237,122,53,194, + 222,13,14,193,125,27,199,251,16,246,54,244,215,7,32,51,125,138,242,97,200,155,193,4,227,58,212,229,173,208,241,24,210,124,162,199,25,111,159,133,252,14,202,253,142,93,247,158,5,183,200,215,17,119,24,60,141,188,191,4,83,112,175,132,142,6,100,31,228,201,224,215,8,255,33,252,143,129,45,240,111,1,71,225,126,37,228,140,221,54,61,24,163,128,96,67,15,120,100,131, + 227,94,3,158,128,251,41,240,115,198,47,55,56,121,68,206,65,89,231,44,161,44,181,129,178,212,6,125,242,109,208,156,63,206,9,145,231,156,37,148,165,54,80,150,218,160,111,169,13,150,218,64,89,106,131,190,165,54,88,106,3,101,169,13,250,254,48,219,224,55,120,118,123,1,207,120,231,194,125,238,9,130,162,226,185,19,235,218,229,144,25,236,141,156,143,176,243,125,88,137, + 52,171,85,103,239,112,18,207,204,159,1,69,132,23,193,122,132,247,33,254,75,108,79,228,171,144,247,130,7,192,38,196,157,14,202,72,247,10,200,97,200,87,67,94,1,249,102,240,58,184,255,18,242,175,193,251,193,118,248,53,240,65,184,13,48,4,247,101,96,12,238,203,33,175,2,39,219,251,80,125,216,23,0,239,132,255,189,224,58,96,128,119,161,157,15,65,94,3,121,13, + 248,20,210,220,6,255,23,32,191,8,254,30,124,9,124,25,252,3,184,7,124,97,41,78,89,106,23,101,105,76,244,45,93,15,75,115,129,18,219,92,48,137,121,247,211,224,179,224,106,220,23,174,78,17,29,123,165,122,138,236,93,66,89,106,3,101,169,13,214,187,183,65,152,245,244,63,168,11,238,127,132,251,94,240,29,48,3,30,81,157,247,212,54,223,64,252,19,156,255,219, + 1,60,128,244,218,50,199,253,83,150,239,127,66,62,7,158,183,215,151,235,240,94,13,178,107,197,226,124,43,224,63,9,212,177,222,189,30,107,206,45,88,159,158,5,250,17,118,6,248,163,21,233,206,61,54,15,176,245,120,6,246,157,223,235,216,105,191,139,125,193,126,46,57,9,235,102,112,15,120,18,156,134,118,168,130,113,48,11,186,215,226,93,37,248,47,224,159,192,111,64, + 55,218,226,92,240,46,112,15,248,53,56,7,122,222,2,198,193,44,88,137,231,139,34,56,104,239,161,171,138,114,39,120,18,156,134,246,168,130,15,131,7,153,124,142,201,15,115,188,106,37,222,215,195,230,14,240,118,184,63,191,114,113,188,205,115,8,91,181,106,193,191,19,238,27,193,247,192,170,44,252,224,70,112,103,71,188,124,143,149,217,181,26,239,179,87,59,238,18,147,119, + 50,62,8,255,167,192,207,184,240,173,57,39,172,157,108,203,29,223,110,75,40,75,109,176,98,169,13,150,218,64,249,131,104,131,107,83,158,3,21,156,151,82,82,226,190,92,186,207,94,39,2,127,138,62,120,29,216,190,194,57,191,83,199,186,166,206,208,16,54,4,222,0,142,96,29,243,3,240,70,184,247,131,17,236,221,190,157,229,217,193,241,238,21,206,218,232,125,144,31,0, + 143,193,125,35,228,223,130,219,192,36,120,26,97,159,97,121,103,177,134,249,49,56,104,159,3,234,195,115,50,194,191,6,190,9,190,5,126,221,231,172,133,94,128,60,226,162,47,131,53,87,38,128,31,35,223,83,224,231,46,249,255,13,97,207,131,23,193,74,164,237,178,215,77,43,157,184,181,144,167,128,51,153,127,7,232,91,235,172,191,94,3,54,98,45,247,122,200,243,65,17, + 156,140,184,45,107,157,116,175,132,44,35,236,28,200,97,200,43,192,219,192,187,192,53,92,121,231,34,254,92,112,61,11,123,202,110,139,149,88,43,162,61,198,33,207,71,220,199,184,244,59,124,184,11,233,138,72,255,57,46,125,25,254,123,224,255,103,46,236,126,184,167,193,67,96,24,241,251,192,143,224,126,18,92,9,247,85,224,89,184,255,5,188,19,238,127,135,252,29,232,196, + 218,241,189,240,191,31,124,16,168,240,247,130,245,171,22,202,222,4,247,203,193,40,226,255,4,242,207,193,235,193,5,171,156,53,168,104,243,45,72,119,27,120,3,226,222,8,38,225,254,36,216,239,146,118,7,248,60,226,222,142,184,119,179,248,175,192,255,53,240,62,248,63,0,238,91,235,172,115,255,150,197,255,29,228,39,61,202,218,1,166,145,254,243,66,252,207,209,7,31,67, + 251,127,5,225,223,4,223,2,15,114,105,30,134,251,81,240,19,240,16,242,255,0,252,15,184,31,135,252,41,248,95,112,255,2,242,255,248,232,221,193,248,127,72,147,193,154,121,37,56,9,108,0,167,102,23,226,183,194,253,10,240,106,240,58,144,7,5,46,126,135,15,37,143,116,187,17,190,25,99,119,4,242,87,176,243,87,109,228,205,130,77,143,99,78,121,156,241,151,62,246, + 252,181,100,157,119,252,30,243,91,180,195,111,99,224,69,38,187,214,121,167,177,159,99,127,235,18,254,95,79,128,126,200,193,182,92,11,172,21,252,31,106,177,46,69,220,139,138,17,185,9,186,111,5,119,112,54,124,2,238,79,131,83,96,219,102,240,247,112,255,19,56,156,80,155,255,27,230,185,187,236,179,183,217,197,225,103,65,247,89,160,129,240,175,67,62,194,197,63,1,247, + 217,8,59,251,15,148,159,181,161,254,175,109,145,127,97,253,244,239,66,127,238,136,153,109,208,181,221,135,223,49,253,203,176,199,178,10,104,8,211,36,89,179,58,126,123,227,102,8,118,14,49,54,194,222,83,193,214,54,218,93,229,206,239,87,5,254,120,181,35,255,140,201,49,216,248,81,112,231,58,199,127,46,194,95,128,124,1,156,239,98,115,145,229,171,130,50,220,195,46,105, + 254,175,189,247,216,139,53,237,106,103,223,244,109,144,239,2,215,128,223,217,123,189,171,177,134,5,227,128,16,191,162,119,33,207,139,108,175,117,37,248,45,220,157,189,78,158,9,164,237,131,251,100,112,6,120,5,88,15,54,129,173,224,85,224,19,72,115,63,234,145,217,24,141,79,163,140,207,174,115,228,151,109,247,106,236,157,175,78,127,92,201,242,207,176,245,91,62,246,94,134, + 58,61,136,248,135,67,214,233,114,228,155,123,9,180,195,85,176,243,42,23,126,202,108,127,13,198,202,235,193,47,218,80,151,95,65,199,89,88,75,255,134,211,165,96,95,161,43,231,157,103,21,226,214,130,83,192,153,224,85,224,53,44,253,185,62,249,118,48,102,236,107,55,183,240,55,44,5,184,75,160,12,246,130,179,237,119,12,27,157,191,91,234,101,82,207,57,239,131,222,2, + 249,159,193,187,192,53,224,122,22,126,47,199,140,240,55,50,47,67,25,167,131,87,128,18,23,254,106,248,95,11,182,129,129,141,11,225,151,192,189,7,236,3,186,173,127,163,19,254,14,200,119,51,247,12,184,6,238,26,243,223,0,121,16,182,140,231,156,185,167,201,199,56,255,93,112,127,14,220,195,194,190,206,228,253,144,223,229,234,97,162,172,143,176,114,191,207,218,233,78,248, + 31,97,238,42,87,246,19,224,103,224,151,224,95,89,25,85,97,78,253,15,166,167,210,227,188,115,225,219,234,222,54,211,179,38,253,115,123,73,177,134,181,237,57,39,128,45,231,36,204,198,53,254,237,144,180,254,115,78,96,54,167,92,255,179,82,190,198,239,61,1,56,39,166,107,249,222,151,32,61,109,28,127,103,191,132,219,233,222,152,218,250,181,224,18,172,89,46,97,236,16, + 214,59,27,216,119,28,54,184,176,109,205,241,233,95,106,236,245,56,251,179,247,15,128,129,53,233,239,233,164,77,41,211,113,140,109,249,46,9,50,202,182,90,143,178,237,112,183,67,30,110,169,124,93,129,236,175,169,74,165,214,161,84,14,99,189,171,144,178,47,223,9,63,128,187,130,176,97,60,222,236,169,57,113,85,59,78,233,84,118,43,153,121,89,57,220,161,12,29,238,148, + 162,98,167,87,58,149,170,93,142,226,48,175,87,33,156,23,107,15,118,93,42,168,195,30,212,103,4,122,119,129,17,48,133,184,169,54,48,12,253,85,244,229,158,60,36,244,110,67,251,255,161,255,219,230,54,46,15,199,55,190,131,216,175,216,99,16,207,174,24,243,251,48,54,246,49,116,244,79,165,214,173,140,0,29,110,235,80,183,114,217,97,167,223,182,163,15,117,54,118,116, + 228,171,34,125,25,233,202,243,233,51,74,129,229,173,176,252,101,96,33,255,94,228,47,51,127,217,142,87,236,179,146,201,141,247,42,116,212,125,226,167,18,166,10,253,83,1,105,234,191,231,114,42,166,52,245,19,88,214,125,234,53,229,147,166,46,184,235,46,121,234,33,242,76,5,164,159,10,136,155,146,212,85,119,73,51,5,44,23,123,44,129,58,120,144,203,255,160,135,123,42, + 130,156,138,177,127,167,124,108,171,199,168,167,126,130,200,41,15,234,62,253,94,119,105,147,122,68,125,83,47,17,172,54,235,155,150,96,0,247,223,129,0,70,4,6,108,106,221,32,195,100,247,98,191,194,165,113,201,63,210,164,230,220,243,143,161,176,176,121,153,113,181,119,196,142,83,22,179,41,227,208,212,53,32,196,143,180,136,137,53,143,185,132,50,223,6,74,135,35,243,33, + 176,243,40,30,132,41,39,239,80,92,65,74,1,20,153,28,176,251,105,197,130,159,176,70,36,48,192,252,5,70,5,207,82,69,46,158,24,35,43,156,126,182,227,42,76,22,25,132,248,34,39,139,172,172,98,12,16,87,102,209,135,66,130,20,5,119,209,69,103,81,82,214,151,97,190,1,245,144,178,224,161,175,234,210,222,5,183,246,95,254,210,166,144,50,197,19,68,234,160,116, + 97,215,60,38,187,214,75,112,95,204,40,158,32,118,22,19,144,197,22,243,22,78,16,251,11,17,36,97,30,92,217,73,109,129,216,156,223,3,119,191,7,221,39,0,253,157,157,202,250,237,153,99,246,110,70,216,102,112,234,246,206,121,127,110,103,215,75,142,245,59,51,243,178,127,21,29,11,235,231,221,55,118,204,75,18,238,207,36,160,238,164,182,65,1,186,251,97,127,255,170, + 227,211,249,113,93,7,29,199,118,196,220,9,121,39,99,226,218,206,121,110,7,21,172,91,38,58,58,149,73,132,79,2,115,27,230,70,73,42,216,47,190,14,146,160,119,226,90,236,219,193,111,160,12,35,69,6,20,249,180,205,251,128,41,195,54,57,198,238,235,148,98,194,238,131,251,24,130,77,70,202,109,104,156,64,76,96,92,25,1,227,114,187,226,63,118,15,32,238,0,23, + 63,233,146,214,96,250,198,208,31,99,18,24,156,141,147,12,195,182,119,115,107,216,215,208,117,144,6,48,117,216,195,220,70,155,49,161,219,140,9,98,115,146,193,235,232,120,105,163,109,57,49,41,93,129,245,172,36,26,210,83,136,123,10,113,28,249,97,167,20,214,6,236,69,165,76,99,79,87,32,211,72,55,13,142,130,185,121,58,149,185,143,116,204,135,17,171,115,3,233,158, + 216,211,57,79,227,141,200,199,49,125,10,244,248,248,27,12,98,101,205,33,126,174,69,8,249,181,62,244,119,194,148,202,24,39,12,98,118,87,16,78,226,181,188,217,31,59,61,165,140,145,192,28,64,33,202,165,16,182,154,146,107,12,147,191,111,17,230,236,243,186,148,81,6,217,182,17,116,251,64,205,182,33,119,127,88,40,32,47,177,120,226,251,133,220,109,178,235,98,192,63, + 202,213,201,20,32,150,118,148,171,51,113,126,147,149,65,1,237,61,202,229,37,151,54,49,56,251,76,65,247,216,183,176,14,144,192,96,249,111,255,27,172,183,193,56,220,227,34,127,131,245,183,157,230,60,39,253,4,211,71,205,249,231,34,204,33,96,6,16,252,22,246,165,44,23,72,168,139,37,132,91,30,249,172,0,40,32,47,9,186,44,15,55,177,186,16,228,12,171,207,12, + 171,91,131,133,19,71,67,8,111,120,164,109,184,228,37,151,118,176,4,187,200,239,62,247,32,238,99,18,52,152,61,22,87,126,3,254,185,128,242,137,191,238,47,194,216,2,163,128,236,113,183,2,99,38,0,18,210,145,100,62,67,162,44,195,39,158,60,210,18,87,23,3,254,81,86,159,81,86,55,83,128,92,234,61,202,165,167,230,245,184,34,218,125,196,206,111,0,83,40,119, + 12,125,55,38,193,4,242,222,126,145,179,47,226,167,195,244,169,103,238,178,46,37,203,32,248,213,151,225,249,222,7,98,229,170,30,126,53,36,20,144,151,4,93,170,143,77,57,212,65,133,63,11,153,99,117,202,9,16,75,155,229,234,76,156,63,199,133,145,11,57,174,108,18,226,84,15,59,115,66,185,189,63,238,148,98,51,203,223,231,1,73,142,179,198,229,152,3,78,103,115, + 11,164,229,2,241,115,210,233,139,195,101,210,18,115,147,16,70,46,233,200,67,15,185,132,91,18,144,80,223,25,212,119,6,52,152,156,1,228,18,63,195,194,103,56,127,131,75,107,185,148,77,92,25,36,217,254,71,158,194,124,44,193,44,244,205,50,142,122,240,196,229,157,243,146,220,230,234,55,97,142,226,48,129,113,6,230,24,31,200,158,35,206,240,246,135,133,34,230,55,88, + 25,212,156,191,206,192,252,197,234,98,10,245,27,5,196,234,109,50,72,240,155,44,140,60,48,185,52,6,116,25,12,114,155,75,207,112,226,248,60,100,207,215,63,197,124,44,1,249,216,49,241,137,140,50,142,178,199,61,152,4,86,14,247,239,18,198,41,99,206,204,204,135,89,62,144,61,142,115,222,254,176,80,136,252,228,145,150,154,243,82,9,243,18,252,51,144,13,48,227,2, + 177,116,51,156,127,134,185,27,12,242,105,215,25,46,31,121,96,9,182,54,132,114,143,52,112,125,74,64,92,25,36,57,55,16,71,110,23,238,19,32,203,80,215,97,254,15,128,144,79,245,241,171,33,200,50,253,57,14,66,121,89,206,38,18,252,57,102,231,102,166,155,56,84,161,236,62,200,62,15,84,142,28,167,155,64,239,35,184,31,74,208,231,83,126,31,99,243,45,157,190, + 109,217,24,193,120,97,144,221,159,155,208,159,49,64,252,120,219,20,79,153,124,217,86,136,116,36,97,7,113,246,54,88,91,16,127,93,141,44,180,147,29,223,96,16,151,126,134,75,51,227,2,121,221,47,127,130,235,73,2,18,108,153,19,236,38,9,140,45,106,170,152,130,219,244,128,4,187,247,225,28,185,225,81,230,129,128,178,76,48,129,125,214,177,167,113,95,146,96,226,138, + 204,60,228,214,126,107,160,19,16,115,19,23,102,112,144,71,184,17,1,10,208,105,184,96,10,110,51,0,131,149,79,62,245,58,192,164,233,81,6,53,215,9,15,161,61,37,32,15,125,22,250,204,242,129,236,107,122,139,127,154,168,144,208,255,13,236,217,55,90,96,6,227,169,225,18,62,199,133,91,46,250,27,33,116,80,4,251,26,49,65,176,225,40,234,116,212,102,11,214,240, + 46,52,144,238,168,71,28,217,249,183,56,238,185,187,50,14,87,48,137,48,29,41,116,142,185,45,29,243,144,203,181,90,239,193,217,51,23,136,197,145,71,30,98,238,41,184,167,34,96,72,64,246,184,239,57,62,204,45,220,16,210,24,156,52,66,230,55,132,114,140,22,243,27,39,120,253,140,16,118,25,109,172,159,17,51,196,116,154,3,120,86,99,76,32,124,98,0,247,50,91, + 246,184,51,30,51,196,223,43,123,162,213,105,82,18,242,88,235,20,113,230,180,152,0,196,202,47,216,103,90,151,29,79,51,126,255,222,140,162,109,196,123,188,54,48,188,81,81,246,236,21,214,42,120,79,105,72,82,201,103,142,113,51,252,55,51,140,0,200,103,173,169,98,190,86,67,66,46,249,114,184,95,228,24,20,102,173,187,18,54,198,128,89,196,245,196,168,176,54,34,9, + 253,106,23,236,143,129,220,5,168,59,160,144,107,125,11,239,125,173,144,144,144,143,236,53,133,142,123,59,160,144,250,179,57,213,149,156,224,206,73,210,95,202,48,58,148,158,166,63,135,115,121,144,167,150,240,156,203,210,17,211,223,139,61,138,94,9,200,195,254,62,148,221,39,1,121,228,159,192,51,246,4,168,216,127,203,93,235,80,38,118,57,99,231,214,91,48,39,219,113,183, + 32,204,102,29,198,24,158,255,77,198,117,235,156,245,204,24,158,241,199,36,32,175,241,191,14,227,55,97,200,239,250,91,23,156,223,174,175,1,73,110,249,187,144,38,1,72,114,252,106,189,152,91,83,130,160,191,52,132,115,31,140,65,129,146,75,56,9,246,15,114,105,7,61,40,113,144,144,95,103,118,16,163,50,134,113,12,134,142,116,74,161,157,174,198,2,53,251,227,116,239, + 56,25,12,164,55,66,64,17,243,27,146,229,18,99,12,239,68,198,36,24,23,202,152,132,127,18,88,25,204,217,9,209,56,31,247,0,48,195,104,0,98,250,231,16,63,7,52,60,179,107,49,51,8,74,156,44,121,64,246,245,178,102,113,250,160,60,37,46,221,160,64,73,18,98,109,80,186,164,75,25,194,94,198,144,4,228,209,255,67,17,243,87,96,79,197,131,1,159,56,98, + 249,205,48,103,176,243,11,220,196,185,41,228,26,129,98,100,116,59,222,145,1,147,201,81,96,116,98,206,247,128,196,235,239,126,92,95,18,76,108,199,253,219,166,19,247,240,166,123,187,195,36,194,38,37,48,153,157,166,15,147,18,229,16,127,191,34,92,131,46,148,112,126,105,16,148,24,131,28,37,33,174,196,197,81,64,123,87,80,118,133,67,197,253,66,229,200,226,158,146,245, + 33,23,64,86,72,71,156,238,254,33,172,253,4,122,113,207,233,149,128,184,114,84,206,222,177,31,161,127,37,48,240,188,101,184,80,224,254,86,171,224,1,249,180,103,29,207,178,245,54,65,46,58,251,177,222,239,247,129,2,198,131,201,205,3,21,132,152,156,223,148,100,48,32,94,67,185,26,135,233,18,166,185,196,13,10,101,80,128,253,102,130,144,135,174,2,206,46,21,124,40, + 6,80,240,128,88,253,138,30,249,136,197,23,2,244,19,43,131,60,100,33,33,251,11,18,118,145,132,253,23,5,80,240,40,187,224,97,255,69,96,132,197,141,112,20,66,230,31,113,201,87,112,169,87,209,165,223,70,124,242,23,36,218,127,132,131,92,218,145,130,236,255,112,230,88,222,237,53,231,239,127,120,200,14,207,35,92,130,178,226,124,243,193,206,67,160,92,235,158,15,43, + 11,84,57,134,145,110,79,94,81,166,15,177,180,118,158,26,135,100,126,130,156,150,252,134,198,52,7,241,115,72,30,247,150,8,152,104,3,179,133,181,215,77,28,166,139,223,12,1,69,88,127,61,140,246,120,152,81,103,223,36,121,88,160,140,62,209,145,90,119,153,131,171,136,171,10,232,46,84,89,223,233,62,80,27,214,155,122,128,13,122,194,246,232,33,244,235,33,108,212,99, + 46,87,119,209,163,75,164,91,169,182,142,173,163,27,178,59,37,200,94,91,45,91,252,253,11,155,163,30,212,133,180,141,29,120,198,102,212,89,24,185,140,129,185,15,226,253,231,14,188,27,101,204,34,221,28,11,155,194,154,110,202,5,66,190,41,143,247,38,83,92,184,101,159,193,59,163,117,220,202,39,198,52,226,231,222,36,183,143,238,70,195,62,235,217,19,237,253,146,204,251, + 52,242,193,8,200,111,36,28,191,155,189,139,24,134,187,194,189,191,169,184,161,176,119,60,77,153,119,190,211,104,83,1,212,202,252,115,16,249,219,0,37,164,139,78,144,250,82,92,247,131,26,202,11,1,185,148,161,173,234,81,180,27,241,204,20,36,111,76,152,48,122,87,5,196,1,98,245,51,217,58,103,36,223,49,207,192,78,7,59,45,185,180,135,122,61,246,6,218,4,217, + 250,14,194,29,3,196,141,135,178,128,30,97,124,144,223,124,184,186,39,18,155,97,247,230,148,32,123,236,31,196,120,105,51,100,183,115,205,65,189,59,60,182,221,196,216,12,255,230,152,32,137,254,86,79,130,13,17,24,123,6,251,105,18,24,103,226,158,119,166,156,77,97,176,176,127,106,185,240,176,36,13,236,209,54,192,28,220,115,216,251,157,19,32,73,59,234,18,223,200,164, + 152,235,78,130,110,139,123,166,157,98,126,75,64,195,30,160,198,24,196,121,128,65,80,98,12,2,29,225,58,71,73,56,51,80,198,255,101,23,116,198,190,154,243,109,105,62,15,241,243,75,63,198,65,140,76,76,96,207,126,158,142,121,40,160,173,6,247,161,158,28,37,6,5,228,171,236,195,122,11,12,61,129,247,55,18,144,215,245,118,10,174,27,15,114,111,196,30,185,36,89, + 6,133,24,39,253,182,158,226,178,182,67,156,13,217,139,187,148,254,213,216,139,118,227,98,188,3,104,178,122,113,156,157,119,0,255,247,126,31,251,255,18,144,88,247,213,78,57,198,122,188,191,21,48,193,1,151,112,195,133,177,71,49,159,73,96,184,228,37,187,255,151,161,15,2,200,225,121,49,231,66,150,145,243,240,147,204,124,191,108,177,30,25,91,178,92,217,189,223,65,251, + 74,64,205,118,95,134,118,199,115,108,63,67,59,3,239,92,129,6,40,161,249,144,24,26,211,51,0,42,12,117,61,234,37,73,118,55,218,149,145,101,244,239,70,61,128,138,248,30,70,150,165,33,73,187,140,128,231,85,35,98,60,181,160,219,144,124,94,157,138,225,27,199,133,21,139,253,246,253,106,4,165,143,184,220,55,10,24,63,133,8,168,66,127,102,133,254,204,9,125,155, + 21,250,178,23,215,114,175,4,228,117,189,173,196,28,200,65,33,199,176,122,16,249,98,130,196,178,235,8,111,51,36,94,163,7,163,173,185,211,88,227,83,138,76,249,92,87,20,114,108,117,99,205,219,45,64,110,99,112,167,243,221,71,217,239,48,158,182,74,46,157,219,247,27,85,159,112,226,226,201,195,173,6,96,68,252,86,209,29,12,138,225,254,100,196,244,237,36,138,80,22, + 37,124,15,54,66,218,115,251,181,206,183,51,197,189,30,179,69,42,74,39,246,73,237,189,85,251,55,158,216,239,52,225,28,111,5,12,179,247,134,149,195,222,237,96,199,15,11,178,130,235,176,2,134,5,127,133,177,139,163,210,140,207,59,229,85,176,95,85,177,169,57,236,230,202,25,246,208,83,1,85,176,155,201,42,131,236,242,106,220,111,90,177,48,10,104,127,138,105,236,25, + 17,199,220,118,188,107,166,249,58,116,28,171,231,62,174,46,21,48,204,181,69,213,14,203,99,207,156,201,10,228,144,36,21,214,238,85,86,78,117,190,252,142,216,246,234,212,22,160,69,247,121,242,77,163,198,24,79,156,222,125,236,119,161,170,104,143,17,182,103,184,15,254,125,246,251,108,197,121,199,215,124,87,91,101,232,12,66,60,73,176,157,59,31,160,43,206,251,223,102,156, + 133,51,91,86,8,168,133,60,86,74,101,19,87,30,69,44,155,90,108,47,43,166,252,86,4,157,196,81,193,59,171,10,131,132,184,50,254,47,39,12,113,250,116,110,44,235,94,172,144,163,204,208,57,72,86,135,178,24,59,31,53,109,92,209,158,115,16,94,232,130,109,122,27,48,185,51,44,26,246,152,181,54,67,92,253,173,46,140,229,136,80,132,246,55,217,153,170,114,155,32, + 65,255,108,150,22,65,18,54,91,72,103,53,81,163,243,176,100,26,114,177,69,195,30,177,150,0,165,10,246,141,5,200,77,255,153,72,159,2,100,207,181,251,177,223,182,21,126,14,18,237,219,186,56,94,164,114,101,70,185,20,242,82,1,146,28,191,198,203,176,230,75,1,98,250,199,126,140,253,88,9,200,195,254,34,202,42,198,8,9,229,23,16,86,104,35,36,232,87,177,175, + 167,250,64,1,253,171,98,61,175,70,128,66,204,133,253,49,60,175,26,88,151,24,9,65,41,234,167,152,234,79,17,219,151,130,198,203,114,244,187,11,196,197,147,79,58,53,38,136,211,65,130,125,196,185,45,140,57,43,1,8,229,207,109,195,89,63,184,103,125,208,54,164,135,109,35,249,80,70,154,114,0,20,113,188,88,43,209,94,49,65,92,121,228,162,107,22,225,179,2,218, + 201,201,80,246,128,56,123,42,195,25,197,192,122,212,104,3,228,210,30,19,118,220,218,100,17,117,142,35,108,156,139,31,143,8,9,229,142,135,196,194,59,227,6,222,89,55,24,132,178,26,156,155,4,26,92,218,6,176,144,223,226,32,123,12,174,246,246,39,5,185,216,71,46,182,207,34,237,108,0,228,115,189,206,225,221,253,156,31,171,253,243,27,125,232,119,1,179,140,189,92, + 238,183,19,168,217,167,125,232,35,129,137,50,206,130,184,49,43,55,23,105,120,7,162,37,0,241,215,245,118,236,127,120,96,97,14,176,18,128,154,115,220,201,232,67,31,44,60,219,88,49,65,110,115,108,63,244,248,160,194,6,53,6,200,167,143,251,17,223,207,65,33,239,73,125,200,211,231,1,69,189,223,109,68,219,5,64,17,117,144,15,7,241,204,126,48,38,168,5,253,55, + 32,223,13,49,65,62,122,198,17,63,238,130,134,51,82,26,71,9,103,172,74,28,20,96,255,208,227,56,255,37,1,121,217,213,1,59,82,130,236,241,183,21,99,76,2,66,218,198,149,184,143,128,71,177,71,64,205,241,187,117,33,190,21,202,167,97,29,228,129,6,40,32,191,138,52,106,12,228,170,56,7,34,73,31,210,247,113,244,187,64,49,93,159,6,206,180,24,17,25,247, + 129,98,158,79,52,236,33,104,9,65,18,250,75,231,227,186,245,160,114,62,238,185,54,25,236,115,9,144,56,46,51,24,131,17,24,122,0,215,189,4,20,161,173,75,168,83,255,37,56,159,6,40,68,190,78,238,236,136,129,177,106,68,132,124,116,141,61,137,253,54,9,200,107,60,117,161,239,61,40,93,128,126,21,24,116,9,43,9,144,61,111,127,27,237,47,65,229,2,140,23, + 14,13,122,7,153,30,13,110,10,104,107,11,207,84,86,76,52,46,197,185,117,200,185,155,177,110,244,120,134,35,23,102,55,97,173,21,35,20,102,62,200,161,175,98,128,90,44,107,176,132,190,98,148,56,6,185,176,65,46,188,82,66,63,219,228,48,47,112,238,18,251,214,190,134,115,133,154,7,196,226,201,37,29,5,228,213,2,160,16,249,43,56,215,72,94,247,147,77,184,102, + 35,48,58,210,117,12,51,0,114,155,15,126,130,235,93,2,242,176,63,119,17,238,193,32,203,32,132,169,120,103,170,250,64,44,175,234,225,87,67,66,1,121,73,208,165,122,164,49,243,78,93,108,127,150,213,39,199,213,45,203,213,145,64,214,195,159,229,194,84,159,247,199,89,46,29,113,168,46,246,101,133,114,115,204,182,156,11,228,210,63,57,143,120,10,96,116,15,198,22,48, + 57,70,153,52,176,191,104,112,76,236,193,115,62,207,6,82,198,126,136,241,35,193,4,203,67,208,57,129,124,166,80,62,133,176,153,98,36,183,31,237,14,114,12,21,239,246,84,208,207,36,5,228,239,125,6,231,147,37,32,175,249,122,19,230,144,22,25,196,53,63,200,40,9,16,43,127,144,75,51,200,197,15,114,238,202,29,152,115,71,24,119,224,28,151,205,38,204,107,77,70, + 184,248,144,104,200,175,113,148,4,251,74,187,160,223,254,230,231,46,128,111,92,106,30,223,185,36,15,134,30,193,186,65,2,242,200,63,97,127,103,244,150,112,120,149,69,45,160,158,142,113,38,73,238,114,140,85,144,99,50,203,220,57,23,178,76,170,62,191,87,69,246,248,125,10,227,83,2,74,232,250,211,58,212,182,65,205,49,215,161,30,99,144,81,18,24,116,9,43,113,101, + 80,115,252,221,135,241,37,65,25,121,203,160,130,119,82,21,134,14,191,218,141,190,13,65,182,128,190,101,228,36,200,114,238,126,228,39,193,254,35,152,155,142,72,48,139,185,112,54,0,10,217,247,22,242,88,246,59,192,229,238,28,75,183,124,177,59,40,143,229,226,182,90,100,230,194,46,87,8,101,207,112,110,2,13,184,27,44,172,1,44,150,223,98,178,1,230,46,196,30,61, + 131,36,218,232,200,119,209,254,18,144,87,27,175,71,61,124,32,201,116,86,4,102,118,163,77,24,13,1,106,182,221,110,199,207,167,157,97,97,22,202,152,187,21,207,96,172,44,74,104,46,34,23,42,47,199,253,239,205,184,94,121,238,6,8,47,189,57,216,22,11,207,235,13,236,97,53,92,152,97,52,56,44,164,183,56,230,238,196,88,169,58,4,233,162,4,80,237,119,6,253, + 201,226,167,223,34,180,67,0,36,153,206,138,200,204,121,232,35,31,200,30,199,231,185,199,53,243,90,40,103,238,60,244,167,0,73,246,199,145,111,225,122,151,96,22,122,102,91,132,56,125,115,227,176,111,188,67,177,240,238,207,138,1,106,94,239,101,180,71,11,144,61,239,150,253,211,240,241,51,66,94,10,192,192,57,71,195,3,226,226,41,32,173,17,1,83,195,243,151,11,196, + 217,105,50,191,201,197,153,30,249,76,96,176,114,41,160,254,99,22,158,215,36,32,143,252,26,244,104,46,16,23,71,62,233,180,152,24,68,93,7,5,8,122,75,144,37,230,30,100,225,37,78,106,30,231,92,137,49,132,186,15,73,64,73,205,199,125,152,51,99,34,135,107,34,7,178,140,126,132,245,3,138,211,222,147,162,149,247,196,199,157,223,182,141,10,181,160,123,238,114,156, + 15,194,187,54,163,13,144,135,13,55,35,238,230,22,33,228,159,192,187,195,9,1,10,209,6,134,164,157,148,16,22,158,89,172,22,33,228,191,191,187,195,7,69,185,191,16,80,127,252,173,173,225,1,181,163,254,157,168,75,194,144,159,254,117,72,147,18,4,253,71,176,119,115,68,2,18,175,221,93,88,183,0,138,216,254,170,253,205,118,138,78,179,188,28,214,128,57,129,254,16, + 235,191,105,140,187,105,142,185,157,25,229,104,136,177,120,228,123,104,47,9,8,105,103,81,238,172,7,100,143,141,85,232,167,152,33,183,49,184,42,217,242,195,208,135,51,139,125,9,209,255,6,188,91,182,89,11,55,32,183,249,232,100,204,61,146,144,144,215,28,198,58,16,225,163,144,163,45,66,62,109,99,34,222,228,56,0,93,7,66,210,243,166,14,165,103,175,243,183,196,3, + 192,204,5,127,115,138,103,176,136,181,28,160,86,175,247,79,224,122,77,16,10,208,111,224,221,167,145,0,20,99,253,41,226,53,68,62,104,73,127,51,243,70,111,252,236,34,89,251,87,57,223,210,228,191,167,73,33,48,132,239,61,82,80,127,221,134,62,105,35,36,218,219,141,241,37,9,185,213,183,91,62,191,95,57,20,178,60,98,233,39,225,54,177,55,110,130,137,66,230,184, + 239,237,83,130,99,157,92,208,241,191,158,0,20,131,126,74,216,70,61,164,173,212,74,251,174,112,254,118,188,132,247,228,37,134,14,63,181,169,127,203,248,191,28,35,116,130,140,47,93,210,150,114,204,245,47,135,108,147,34,254,47,182,64,193,7,66,185,133,128,52,5,70,81,162,188,66,140,20,153,125,69,206,77,204,79,66,155,144,71,27,145,68,187,21,60,218,132,2,218,134, + 66,180,93,161,197,190,43,250,216,89,244,169,35,73,142,25,10,72,67,33,235,88,136,216,223,69,159,250,20,125,236,39,201,235,132,98,168,15,69,188,30,139,45,244,119,53,160,221,10,156,93,67,135,177,143,27,3,147,167,210,34,204,125,93,199,220,19,251,176,31,199,115,106,180,251,144,93,182,41,64,30,225,166,36,20,241,126,151,195,158,122,206,3,10,136,207,73,66,33,117, + 145,71,90,74,224,126,175,102,177,110,14,9,241,237,55,8,219,60,232,65,218,30,15,136,229,239,157,193,249,29,9,200,197,238,205,45,254,141,17,241,229,108,69,157,66,144,187,18,117,227,232,253,25,236,147,128,92,116,159,122,101,167,162,97,31,69,19,160,230,90,100,45,214,11,1,148,222,128,53,162,7,36,81,255,41,252,63,197,209,192,51,69,195,131,105,110,254,33,198,17, + 204,33,71,36,32,33,223,28,131,98,160,140,255,203,45,162,215,112,246,137,243,239,19,252,101,151,58,83,130,246,148,99,66,143,152,159,88,93,140,14,60,143,114,144,75,152,145,32,196,233,35,230,38,206,54,10,97,15,249,212,135,132,178,73,192,112,137,51,60,242,27,45,216,79,18,229,145,75,126,35,68,223,144,100,30,138,169,109,141,22,251,155,92,218,217,8,208,67,9,234, + 167,16,220,137,50,238,140,80,142,206,190,193,168,75,66,2,122,204,80,72,244,24,116,233,17,203,208,219,0,73,183,71,119,11,80,236,223,141,214,149,229,237,225,198,214,199,142,211,94,212,86,168,5,253,36,97,63,197,92,127,106,19,122,30,125,24,166,13,243,225,210,7,234,175,69,235,195,200,249,149,120,9,221,254,74,2,248,212,151,34,182,31,197,140,30,178,110,20,115,251, + 81,76,246,81,139,249,169,77,227,131,132,254,166,86,251,171,150,32,108,110,161,8,101,80,179,156,60,67,244,231,37,137,88,23,242,169,7,121,196,147,11,186,132,14,242,65,143,1,106,177,124,106,1,61,106,125,149,24,175,25,123,125,174,132,207,19,133,33,201,125,92,242,42,163,22,255,28,45,133,226,232,79,227,247,24,181,132,127,31,134,66,180,131,22,241,247,99,180,151,248, + 111,215,104,105,219,169,56,191,63,224,121,125,40,201,98,255,77,191,25,1,138,168,63,147,239,78,5,98,250,235,61,148,42,197,101,233,34,254,6,104,61,38,57,21,64,157,251,173,181,130,15,197,0,10,17,243,235,236,62,88,180,101,205,249,109,208,249,61,14,133,133,177,240,2,243,23,25,5,70,145,15,171,113,249,240,251,36,58,123,103,90,116,65,231,169,177,125,21,46,174, + 200,108,43,50,127,221,110,179,67,221,139,242,23,184,242,234,107,28,105,135,105,2,5,78,87,65,200,87,117,161,200,165,43,112,249,171,92,57,83,107,28,121,220,222,124,173,219,65,241,136,19,220,43,213,116,177,219,109,202,229,55,113,167,2,198,120,221,227,55,116,167,2,174,129,105,70,189,201,50,132,131,122,74,178,184,28,253,11,138,33,165,6,74,23,118,41,23,115,84,88, + 88,73,130,10,210,14,180,160,183,24,81,14,48,74,204,142,17,140,193,17,197,233,11,190,239,234,33,231,185,122,76,114,58,196,220,89,23,198,227,148,132,28,96,245,29,113,169,179,21,115,253,234,30,215,73,221,231,250,169,7,200,10,214,76,245,67,237,193,254,109,175,10,244,237,201,43,243,191,61,181,11,20,113,222,181,152,34,133,132,57,24,227,247,85,15,182,64,253,204,116, + 89,137,51,11,43,83,164,136,235,178,216,6,10,9,81,124,137,219,95,144,168,79,181,77,186,171,41,183,115,245,247,164,94,213,4,251,168,126,130,48,149,22,61,237,163,2,125,211,98,248,26,119,92,159,7,151,47,172,191,227,98,0,229,14,128,105,143,240,1,142,41,15,91,167,124,24,88,238,31,159,36,211,46,20,2,158,183,47,10,160,16,49,255,84,27,199,219,148,27,107, + 218,135,189,70,159,134,156,230,40,224,153,173,144,34,35,88,3,79,97,109,60,213,6,134,177,254,174,178,245,119,181,230,248,163,174,111,45,156,17,183,34,112,67,140,223,255,191,161,5,166,176,70,157,74,145,110,172,81,187,83,196,58,37,25,26,111,196,25,98,142,105,151,176,6,48,48,31,27,192,196,222,129,105,239,99,228,157,223,138,29,251,110,167,20,19,200,123,128,149,97, + 180,194,186,5,14,112,110,163,93,108,76,150,3,30,76,236,205,204,51,45,204,135,211,109,96,128,187,135,79,167,160,127,154,179,227,178,195,138,178,23,12,99,46,220,131,121,113,216,67,86,216,184,172,52,169,117,204,239,253,84,154,126,69,112,11,236,62,156,153,167,194,151,161,144,178,127,222,173,28,251,125,115,29,12,20,59,20,13,242,82,188,35,27,14,176,107,152,73,157,229, + 29,14,72,183,59,223,113,204,189,135,229,187,20,122,46,13,192,20,222,83,101,139,14,246,111,179,15,136,228,187,142,217,115,41,87,198,128,93,175,188,243,219,239,205,120,227,38,14,251,111,204,111,114,145,110,172,14,136,115,137,175,64,247,128,210,163,28,184,184,99,30,3,97,234,221,241,211,243,230,14,249,244,39,37,75,143,7,253,151,102,230,49,112,15,50,66,112,0,76,236, + 199,220,241,201,142,121,105,132,204,111,8,140,224,26,24,73,136,1,9,140,245,106,36,14,248,196,77,114,24,96,20,152,76,26,140,61,236,218,31,62,54,207,40,243,227,212,224,199,241,106,111,236,241,108,120,93,7,55,181,143,129,230,247,81,46,116,238,227,54,163,192,136,114,111,94,222,58,77,253,38,179,97,148,249,71,57,251,76,15,70,5,76,206,109,216,101,231,133,247,246, + 92,94,35,192,46,21,247,124,181,77,228,118,117,205,163,194,157,133,204,2,107,11,41,71,239,202,28,99,206,102,11,57,242,46,219,221,177,224,190,162,233,119,1,225,71,231,243,117,204,203,163,140,198,21,88,215,129,163,92,216,81,14,35,226,245,102,196,200,40,119,77,154,28,163,46,97,166,75,220,168,224,55,192,216,163,88,151,74,48,177,27,115,168,205,122,204,167,28,134,164, + 173,134,128,41,196,141,114,24,46,97,163,30,105,12,151,58,27,45,96,186,216,60,234,129,201,217,55,38,217,126,163,66,254,73,134,90,92,150,46,7,83,166,158,46,6,246,116,140,20,185,245,2,172,73,240,187,76,198,9,196,36,152,136,171,188,83,210,69,205,225,26,75,145,28,35,203,185,115,46,100,25,70,26,207,244,235,22,99,226,190,107,2,67,8,55,146,224,116,212,57, + 69,180,53,233,98,110,71,91,115,140,2,163,19,237,210,38,76,166,211,116,97,148,147,163,140,156,238,144,5,234,102,92,95,41,144,101,54,228,56,178,140,28,231,207,113,168,44,111,239,209,78,41,84,15,221,26,165,139,218,155,46,214,25,233,178,232,155,131,171,122,218,254,141,195,221,246,179,238,106,143,125,31,81,222,148,48,237,210,115,211,2,42,246,197,212,52,185,62,93,194, + 252,189,128,150,0,106,2,251,127,106,8,140,136,251,103,70,68,172,54,124,207,219,242,65,179,239,1,155,113,126,20,247,20,13,82,99,148,4,191,230,17,166,69,196,232,79,151,193,125,93,243,104,120,23,170,165,192,32,211,63,40,137,41,177,127,102,74,96,120,236,143,77,112,24,182,252,16,158,231,184,61,189,161,239,119,74,161,173,70,253,18,164,116,49,206,59,251,80,177,223, + 235,92,156,81,84,251,153,233,148,214,201,161,172,28,35,11,84,232,86,83,34,199,108,200,114,54,229,56,219,178,46,126,21,103,28,212,20,209,206,192,115,65,138,148,56,89,226,252,154,139,95,115,137,83,177,39,168,166,73,29,125,184,18,123,59,237,162,222,94,125,113,124,67,200,0,119,180,136,145,18,123,216,59,39,203,126,6,233,77,17,236,105,89,105,146,77,23,109,107,186, + 68,57,187,101,197,128,138,61,91,53,77,58,210,197,192,53,96,164,136,245,178,228,56,202,152,245,193,178,207,160,173,77,15,213,30,131,167,182,151,62,70,191,237,223,208,126,250,56,140,62,127,38,3,226,141,136,104,246,51,96,103,122,88,56,11,103,165,136,134,53,190,150,34,150,125,15,216,154,30,218,105,237,167,204,49,120,62,158,215,192,32,208,50,136,111,51,37,166,191,228, + 65,238,18,135,44,80,113,102,81,109,51,89,166,59,203,236,200,113,100,133,240,126,164,239,7,157,182,188,4,231,203,64,233,2,212,131,161,97,189,165,181,153,220,8,108,3,89,160,110,66,157,82,36,203,236,200,50,155,114,1,100,61,236,238,31,65,219,218,108,114,220,98,190,126,132,103,153,187,247,39,120,255,34,129,106,151,117,7,246,43,88,153,253,12,3,126,35,37,76,216, + 111,216,247,168,13,233,50,186,7,239,234,24,102,0,42,246,84,213,20,201,238,199,152,97,228,60,200,114,50,43,164,215,208,238,90,154,172,75,151,65,188,167,31,100,148,2,24,228,210,13,50,169,158,142,126,72,147,238,116,209,216,239,208,151,36,208,144,86,75,144,146,135,222,65,166,187,36,132,87,238,206,28,247,123,247,86,74,52,170,56,71,25,146,25,206,61,87,197,217,77, + 134,133,242,230,238,132,27,168,62,191,123,175,182,1,3,107,62,35,69,44,140,81,43,85,148,5,10,28,98,186,130,24,214,76,231,145,62,105,155,11,238,122,189,126,139,216,104,59,138,98,236,4,17,203,177,82,166,177,19,215,175,7,115,59,113,13,55,89,133,107,26,28,21,48,66,252,30,171,145,0,106,194,191,87,170,6,96,36,244,123,165,134,44,55,225,222,211,230,51,52, + 154,15,106,155,127,143,83,21,48,236,57,162,59,61,90,249,189,35,53,78,182,166,139,248,91,67,90,155,49,183,225,185,8,108,87,22,220,237,228,200,211,157,82,204,110,193,126,180,4,71,175,200,28,251,251,150,6,39,31,219,162,40,63,188,66,57,38,103,16,54,3,74,56,179,80,74,17,83,252,91,165,188,28,205,252,149,26,206,232,49,118,49,134,37,255,38,117,24,82,71, + 191,235,2,131,40,127,80,208,167,187,196,15,10,108,15,192,12,64,103,229,155,18,105,244,152,49,19,210,99,70,248,45,61,51,102,38,241,62,99,146,195,140,169,156,73,151,114,39,3,210,76,50,70,35,252,182,252,104,12,12,197,244,187,141,67,45,242,240,161,238,112,172,161,197,28,106,49,31,227,210,21,233,83,22,208,125,184,25,247,204,155,83,164,220,252,222,107,45,29,100, + 127,179,168,156,4,10,250,0,223,98,208,109,89,235,96,208,162,249,110,56,196,189,103,15,39,203,73,181,151,178,216,190,32,202,168,79,89,2,61,33,202,129,253,79,210,54,150,19,178,187,28,129,113,172,57,199,83,100,22,123,176,179,49,115,52,128,39,62,222,121,140,178,61,198,148,20,105,105,206,33,142,238,72,236,199,185,219,253,94,40,29,139,252,219,149,46,229,58,172,197, + 174,59,188,252,216,26,114,55,194,119,35,221,110,91,230,157,111,182,236,70,185,21,27,101,241,247,98,108,247,101,130,191,194,165,173,216,28,230,64,121,111,58,220,173,92,1,182,187,172,95,247,67,111,165,214,233,143,178,96,195,188,158,60,242,216,40,88,155,51,42,28,85,165,147,251,182,68,199,162,244,21,31,170,76,238,106,134,229,133,111,220,52,81,22,194,230,219,52,31,188, + 78,79,146,76,74,191,127,144,97,116,229,213,84,169,216,227,226,240,194,184,60,54,86,14,47,192,135,93,214,116,231,133,126,117,233,231,221,243,82,153,247,239,182,251,93,225,198,34,220,21,137,251,223,176,248,221,34,197,35,109,208,189,181,198,16,194,171,12,221,135,225,32,221,74,235,52,245,87,57,134,153,190,170,71,124,53,1,134,37,215,69,213,20,116,14,135,148,213,20,219, + 173,218,70,221,85,143,177,162,115,99,75,119,113,235,30,227,80,143,145,170,168,191,230,92,159,77,169,39,68,53,44,181,238,5,89,99,110,62,174,198,133,241,110,69,200,139,223,27,169,218,40,46,233,196,176,26,43,183,137,226,82,94,83,214,92,224,211,40,46,122,196,180,138,79,157,130,168,121,212,57,40,157,18,162,124,62,111,205,167,109,252,252,74,68,226,44,191,38,217,102, + 178,241,74,140,117,140,171,44,37,6,14,71,47,35,169,121,68,247,160,154,130,78,157,211,93,141,25,253,4,169,131,222,42,53,129,208,249,187,23,214,142,53,230,118,67,9,81,14,243,87,221,242,213,66,166,19,210,234,110,241,53,9,251,100,237,175,133,40,79,34,93,53,0,157,215,87,19,244,207,187,201,61,141,226,204,103,209,234,76,137,141,231,106,8,244,128,178,244,192,122, + 116,75,247,223,62,172,81,246,121,81,243,137,11,162,22,16,198,220,186,120,189,214,146,99,164,182,252,56,54,227,204,197,230,20,209,35,142,55,61,42,53,151,177,210,78,148,54,216,35,234,80,18,108,15,175,114,20,89,253,2,243,97,221,199,151,173,120,216,125,92,56,45,158,51,107,112,47,10,167,197,191,87,173,180,1,94,183,34,198,177,58,132,237,55,37,110,27,61,218,179, + 221,237,83,147,161,217,183,62,125,220,12,175,137,233,197,48,209,205,245,147,239,245,74,62,215,5,121,216,76,194,248,235,246,73,79,238,99,166,198,16,127,127,93,17,210,37,65,13,182,242,254,195,216,19,20,152,204,224,28,65,138,232,7,83,166,217,63,181,116,216,12,27,54,167,132,30,131,253,147,56,15,58,153,18,122,202,250,39,153,13,186,95,255,222,157,46,126,103,65,244, + 54,48,41,121,142,105,50,33,116,123,14,172,181,0,63,135,214,98,96,209,188,76,194,125,166,59,2,116,60,121,110,189,20,55,10,35,201,121,81,241,209,225,21,158,231,238,173,110,237,144,119,137,143,147,188,139,110,89,106,49,234,143,131,90,192,88,18,211,230,37,235,210,140,207,71,168,115,179,255,21,137,241,224,151,87,137,56,54,221,168,69,196,175,108,37,65,187,101,168,37, + 132,221,79,121,143,49,22,87,249,53,73,157,113,160,180,185,29,91,209,147,247,185,214,69,148,136,227,198,139,195,220,115,8,206,235,232,77,20,159,122,136,246,214,90,28,15,110,117,175,181,216,159,34,121,143,251,143,23,249,22,239,13,126,113,97,202,23,245,200,232,142,10,95,190,232,22,210,138,255,254,63,0,0,0,255,255,0,0,0,255,255,99,102,0,0}; + } +} diff --git a/src/Avalonia.Base/Metadata/DataTypeAttribute.cs b/src/Avalonia.Base/Metadata/DataTypeAttribute.cs new file mode 100644 index 0000000000..ac46a0d30a --- /dev/null +++ b/src/Avalonia.Base/Metadata/DataTypeAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Avalonia.Metadata; + +/// +/// Defines the property that contains type that should be used as a type information for compiled bindings. +/// +/// +/// Used on DataTemplate.DataType property so it can be inherited in compiled bindings inside of the template. +/// +[AttributeUsage(AttributeTargets.Property)] +public class DataTypeAttribute : Attribute +{ + +} diff --git a/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs b/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs new file mode 100644 index 0000000000..348c983c03 --- /dev/null +++ b/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Avalonia.Metadata +{ + /// + /// This interface is not intended to be implemented outside of the core Avalonia framework as + /// its API may change without warning. + /// + /// + /// This interface is stable for consumption by a client, but should not be implemented as members + /// may be added to its API. + /// + [AttributeUsage(AttributeTargets.Interface)] + public class NotClientImplementableAttribute : Attribute + { + } +} diff --git a/src/Avalonia.Base/Metadata/UnstableAttribute.cs b/src/Avalonia.Base/Metadata/UnstableAttribute.cs new file mode 100644 index 0000000000..3b6fa5168a --- /dev/null +++ b/src/Avalonia.Base/Metadata/UnstableAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Avalonia.Metadata +{ + /// + /// This API is unstable and is not covered by API compatibility guarantees between minor and + /// patch releases. + /// + public class UnstableAttribute : Attribute + { + } +} diff --git a/src/Avalonia.PlatformSupport/AssetLoader.cs b/src/Avalonia.Base/Platform/AssetLoader.cs similarity index 98% rename from src/Avalonia.PlatformSupport/AssetLoader.cs rename to src/Avalonia.Base/Platform/AssetLoader.cs index 0e33c3d4c7..a74da2a178 100644 --- a/src/Avalonia.PlatformSupport/AssetLoader.cs +++ b/src/Avalonia.Base/Platform/AssetLoader.cs @@ -3,11 +3,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using Avalonia.Platform; -using Avalonia.PlatformSupport.Internal; +using Avalonia.Platform.Internal; using Avalonia.Utilities; -namespace Avalonia.PlatformSupport +namespace Avalonia.Platform { /// /// Loads assets compiled into the application binary. diff --git a/src/Avalonia.Base/Platform/IAssetLoader.cs b/src/Avalonia.Base/Platform/IAssetLoader.cs index e3899784ad..b65d61803f 100644 --- a/src/Avalonia.Base/Platform/IAssetLoader.cs +++ b/src/Avalonia.Base/Platform/IAssetLoader.cs @@ -2,12 +2,14 @@ using System; using System.Collections.Generic; using System.IO; using System.Reflection; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Loads assets compiled into the application binary. /// + [Unstable] public interface IAssetLoader { /// diff --git a/src/Avalonia.Base/Platform/IBitmapImpl.cs b/src/Avalonia.Base/Platform/IBitmapImpl.cs index 1e68bc477d..8f11f68e7c 100644 --- a/src/Avalonia.Base/Platform/IBitmapImpl.cs +++ b/src/Avalonia.Base/Platform/IBitmapImpl.cs @@ -1,11 +1,13 @@ using System; using System.IO; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IBitmapImpl : IDisposable { /// diff --git a/src/Avalonia.Base/Platform/ICursorImpl.cs b/src/Avalonia.Base/Platform/ICursorImpl.cs index 14235869f7..74e0ba2e5c 100644 --- a/src/Avalonia.Base/Platform/ICursorImpl.cs +++ b/src/Avalonia.Base/Platform/ICursorImpl.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Input; +using Avalonia.Metadata; #nullable enable @@ -8,6 +9,7 @@ namespace Avalonia.Platform /// /// Represents a platform implementation of a . /// + [Unstable] public interface ICursorImpl : IDisposable { } diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs index 4e6612e908..d84a509234 100644 --- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -3,12 +3,14 @@ using Avalonia.Media; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the interface through which drawing occurs. /// + [Unstable] public interface IDrawingContextImpl : IDisposable { /// diff --git a/src/Avalonia.Base/Platform/IFontManagerImpl.cs b/src/Avalonia.Base/Platform/IFontManagerImpl.cs index 0110287afd..932249bd52 100644 --- a/src/Avalonia.Base/Platform/IFontManagerImpl.cs +++ b/src/Avalonia.Base/Platform/IFontManagerImpl.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; using System.Globalization; using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IFontManagerImpl { /// diff --git a/src/Avalonia.Base/Platform/IGeometryImpl.cs b/src/Avalonia.Base/Platform/IGeometryImpl.cs index ed6de1b5c7..c80f8923ef 100644 --- a/src/Avalonia.Base/Platform/IGeometryImpl.cs +++ b/src/Avalonia.Base/Platform/IGeometryImpl.cs @@ -1,10 +1,12 @@ using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IGeometryImpl { /// diff --git a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs index 08786d9689..7801bdd50f 100644 --- a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs +++ b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs @@ -1,9 +1,11 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Actual implementation of a glyph run that stores platform dependent resources. /// + [Unstable] public interface IGlyphRunImpl : IDisposable { } } diff --git a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs index 6afd79d29c..415f34fb29 100644 --- a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs +++ b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IGlyphTypefaceImpl : IDisposable { /// diff --git a/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs b/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs index e399976bbe..b087724079 100644 --- a/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs +++ b/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IMacOSTopLevelPlatformHandle { IntPtr NSView { get; } diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index c46efd46c3..e39a4e23df 100644 --- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -3,12 +3,14 @@ using System.Collections.Generic; using System.IO; using Avalonia.Media; using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the main platform-specific interface for the rendering subsystem. /// + [Unstable] public interface IPlatformRenderInterface { /// @@ -56,6 +58,13 @@ namespace Avalonia.Platform /// A combined geometry. IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2); + /// + /// Created a geometry implementation for the glyph run. + /// + /// The glyph run to build a geometry from. + /// The geometry returned contains the combined geometry of all glyphs in the glyph run. + IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun); + /// /// Creates a renderer. /// diff --git a/src/Avalonia.Base/Platform/IPlatformSettings.cs b/src/Avalonia.Base/Platform/IPlatformSettings.cs index e4b28e6575..78d1817312 100644 --- a/src/Avalonia.Base/Platform/IPlatformSettings.cs +++ b/src/Avalonia.Base/Platform/IPlatformSettings.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformSettings { Size DoubleClickSize { get; } diff --git a/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs b/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs index 2137f965cc..bf18a7da5b 100644 --- a/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs @@ -1,5 +1,6 @@ using System; using System.Threading; +using Avalonia.Metadata; using Avalonia.Threading; namespace Avalonia.Platform @@ -7,6 +8,7 @@ namespace Avalonia.Platform /// /// Provides platform-specific services relating to threading. /// + [Unstable] public interface IPlatformThreadingInterface { void RunLoop(CancellationToken cancellationToken); diff --git a/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs b/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs index 9add07afe3..d33c503650 100644 --- a/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs +++ b/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; namespace Avalonia.Platform { @@ -5,6 +6,7 @@ namespace Avalonia.Platform /// Defines the platform-specific interface for a /// . /// + [Unstable] public interface IRenderTargetBitmapImpl : IBitmapImpl, IRenderTarget { } diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs index 850757a1ee..8ab04f5995 100644 --- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IRuntimePlatform { IDisposable StartSystemTimer(TimeSpan interval, Action tick); @@ -9,6 +11,7 @@ namespace Avalonia.Platform IUnmanagedBlob AllocBlob(int size); } + [Unstable] public interface IUnmanagedBlob : IDisposable { IntPtr Address { get; } @@ -17,6 +20,7 @@ namespace Avalonia.Platform } + [Unstable] public struct RuntimePlatformInfo { public OperatingSystemType OperatingSystem { get; set; } @@ -29,6 +33,7 @@ namespace Avalonia.Platform public bool IsUnix { get; set; } } + [Unstable] public enum OperatingSystemType { Unknown, diff --git a/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs index 4587979308..3d5ee36608 100644 --- a/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs +++ b/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { /// /// Describes a geometry using drawing commands. /// + [Unstable] public interface IStreamGeometryContextImpl : IGeometryContext { } diff --git a/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs index 5b070fde02..bd4411f3a4 100644 --- a/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs +++ b/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IStreamGeometryImpl : IGeometryImpl { /// diff --git a/src/Avalonia.Base/Platform/ITextShaperImpl.cs b/src/Avalonia.Base/Platform/ITextShaperImpl.cs index 11be9e3f09..10e58b7d0b 100644 --- a/src/Avalonia.Base/Platform/ITextShaperImpl.cs +++ b/src/Avalonia.Base/Platform/ITextShaperImpl.cs @@ -1,4 +1,5 @@ using Avalonia.Media.TextFormatting; +using Avalonia.Metadata; using Avalonia.Utilities; namespace Avalonia.Platform @@ -6,6 +7,7 @@ namespace Avalonia.Platform /// /// An abstraction that is used produce shaped text. /// + [Unstable] public interface ITextShaperImpl { /// diff --git a/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs b/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs index 1ed025b571..2754414cd1 100644 --- a/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs +++ b/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Platform +using Avalonia.Metadata; + +namespace Avalonia.Platform { /// /// Represents a geometry with a transform applied. @@ -7,6 +9,7 @@ /// An transforms a geometry without transforming its /// stroke thickness. /// + [Unstable] public interface ITransformedGeometryImpl : IGeometryImpl { /// diff --git a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs index c4e2e4915f..fa1e1862b7 100644 --- a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs +++ b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Platform +using Avalonia.Metadata; + +namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IWriteableBitmapImpl : IBitmapImpl { ILockedFramebuffer Lock(); diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs b/src/Avalonia.Base/Platform/Internal/AssemblyDescriptor.cs similarity index 97% rename from src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs rename to src/Avalonia.Base/Platform/Internal/AssemblyDescriptor.cs index 64ffec8482..df2a26ddd3 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs +++ b/src/Avalonia.Base/Platform/Internal/AssemblyDescriptor.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Reflection; using Avalonia.Utilities; -namespace Avalonia.PlatformSupport.Internal; +namespace Avalonia.Platform.Internal; internal interface IAssemblyDescriptor { diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs b/src/Avalonia.Base/Platform/Internal/AssemblyDescriptorResolver.cs similarity index 89% rename from src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs rename to src/Avalonia.Base/Platform/Internal/AssemblyDescriptorResolver.cs index 28ae35d57d..b12130b1f7 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs +++ b/src/Avalonia.Base/Platform/Internal/AssemblyDescriptorResolver.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; -namespace Avalonia.PlatformSupport.Internal; +namespace Avalonia.Platform.Internal; internal interface IAssemblyDescriptorResolver { @@ -29,9 +30,8 @@ internal class AssemblyDescriptorResolver: IAssemblyDescriptorResolver } else { - // iOS does not support loading assemblies dynamically! #if NET6_0_OR_GREATER - if (OperatingSystem.IsIOS()) + if (!RuntimeFeature.IsDynamicCodeSupported) { throw new InvalidOperationException( $"Assembly {name} needs to be referenced and explicitly loaded before loading resources"); diff --git a/src/Avalonia.PlatformSupport/Internal/AssetDescriptor.cs b/src/Avalonia.Base/Platform/Internal/AssetDescriptor.cs similarity index 96% rename from src/Avalonia.PlatformSupport/Internal/AssetDescriptor.cs rename to src/Avalonia.Base/Platform/Internal/AssetDescriptor.cs index baae1f99e7..52f8f5e68d 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssetDescriptor.cs +++ b/src/Avalonia.Base/Platform/Internal/AssetDescriptor.cs @@ -2,7 +2,7 @@ using System.IO; using System.Reflection; -namespace Avalonia.PlatformSupport.Internal; +namespace Avalonia.Platform.Internal; internal interface IAssetDescriptor { diff --git a/src/Avalonia.PlatformSupport/Internal/Constants.cs b/src/Avalonia.Base/Platform/Internal/Constants.cs similarity index 69% rename from src/Avalonia.PlatformSupport/Internal/Constants.cs rename to src/Avalonia.Base/Platform/Internal/Constants.cs index c8a0f7b1ce..cad864e7e3 100644 --- a/src/Avalonia.PlatformSupport/Internal/Constants.cs +++ b/src/Avalonia.Base/Platform/Internal/Constants.cs @@ -1,4 +1,4 @@ -namespace Avalonia.PlatformSupport.Internal; +namespace Avalonia.Platform.Internal; internal static class Constants { diff --git a/src/Avalonia.PlatformSupport/DynLoader.cs b/src/Avalonia.Base/Platform/Internal/DynLoader.cs similarity index 82% rename from src/Avalonia.PlatformSupport/DynLoader.cs rename to src/Avalonia.Base/Platform/Internal/DynLoader.cs index ad76ac4724..07903669b1 100644 --- a/src/Avalonia.PlatformSupport/DynLoader.cs +++ b/src/Avalonia.Base/Platform/Internal/DynLoader.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; using Avalonia.Platform.Interop; // ReSharper disable InconsistentNaming -namespace Avalonia.PlatformSupport +namespace Avalonia.Platform.Internal { class UnixLoader : IDynamicLibraryLoader { @@ -26,25 +26,6 @@ namespace Avalonia.PlatformSupport } } - static class AndroidImports - { - [DllImport("libdl.so")] - private static extern IntPtr dlopen(string path, int flags); - - [DllImport("libdl.so")] - private static extern IntPtr dlsym(IntPtr handle, string symbol); - - [DllImport("libdl.so")] - private static extern IntPtr dlerror(); - - public static void Init() - { - DlOpen = dlopen; - DlSym = dlsym; - DlError = dlerror; - } - } - static class OsXImports { [DllImport("/usr/lib/libSystem.dylib")] @@ -77,10 +58,6 @@ namespace Avalonia.PlatformSupport Marshal.FreeHGlobal(buffer); if (unixName == "Darwin") OsXImports.Init(); -#if NET6_0_OR_GREATER - else if (OperatingSystem.IsAndroid()) - AndroidImports.Init(); -#endif else LinuxImports.Init(); } @@ -135,6 +112,39 @@ namespace Avalonia.PlatformSupport return ptr; } } + +#if NET6_0_OR_GREATER + internal class Net6Loader : IDynamicLibraryLoader + { + public IntPtr LoadLibrary(string dll) + { + try + { + return NativeLibrary.Load(dll); + } + catch (Exception ex) + { + throw new DynamicLibraryLoaderException("Error loading " + dll, ex); + } + } + + public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional) + { + try + { + if (optional) + { + return NativeLibrary.TryGetExport(dll, proc, out var address) ? address : default; + } + return NativeLibrary.GetExport(dll, proc); + } + catch (Exception ex) + { + throw new DynamicLibraryLoaderException("Error " + dll, ex); + } + } + } +#endif internal class NotSupportedLoader : IDynamicLibraryLoader { diff --git a/src/Avalonia.PlatformSupport/Internal/SlicedStream.cs b/src/Avalonia.Base/Platform/Internal/SlicedStream.cs similarity index 97% rename from src/Avalonia.PlatformSupport/Internal/SlicedStream.cs rename to src/Avalonia.Base/Platform/Internal/SlicedStream.cs index e310db964a..124c248aa8 100644 --- a/src/Avalonia.PlatformSupport/Internal/SlicedStream.cs +++ b/src/Avalonia.Base/Platform/Internal/SlicedStream.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace Avalonia.PlatformSupport.Internal; +namespace Avalonia.Platform.Internal; internal class SlicedStream : Stream { diff --git a/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs new file mode 100644 index 0000000000..a56d9ffd1c --- /dev/null +++ b/src/Avalonia.Base/Platform/Internal/UnmanagedBlob.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Avalonia.Platform.Internal; + +internal class UnmanagedBlob : IUnmanagedBlob +{ + private IntPtr _address; + private readonly object _lock = new object(); +#if DEBUG + private static readonly List Backtraces = new List(); + private static Thread? GCThread; + private readonly string _backtrace; + private static readonly object _btlock = new object(); + + class GCThreadDetector + { + ~GCThreadDetector() + { + GCThread = Thread.CurrentThread; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Spawn() => new GCThreadDetector(); + + static UnmanagedBlob() + { + Spawn(); + GC.WaitForPendingFinalizers(); + } +#endif + + public UnmanagedBlob(int size) + { + try + { + if (size <= 0) + throw new ArgumentException("Positive number required", nameof(size)); + _address = Alloc(size); + GC.AddMemoryPressure(size); + Size = size; + } + catch + { + GC.SuppressFinalize(this); + throw; + } +#if DEBUG + _backtrace = Environment.StackTrace; + lock (_btlock) + Backtraces.Add(_backtrace); +#endif + } + + void DoDispose() + { + lock (_lock) + { + if (!IsDisposed) + { +#if DEBUG + lock (_btlock) + Backtraces.Remove(_backtrace); +#endif + Free(_address, Size); + GC.RemoveMemoryPressure(Size); + IsDisposed = true; + _address = IntPtr.Zero; + Size = 0; + } + } + } + + public void Dispose() + { +#if DEBUG + if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId) + { + lock (_lock) + { + if (!IsDisposed) + { + Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: " + + Environment.StackTrace + + "\n\nBlob created by " + _backtrace); + } + } + } +#endif + DoDispose(); + GC.SuppressFinalize(this); + } + + ~UnmanagedBlob() + { +#if DEBUG + Console.Error.WriteLine("Undisposed native blob created by " + _backtrace); +#endif + DoDispose(); + } + + public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; + public int Size { get; private set; } + public bool IsDisposed { get; private set; } + + [DllImport("libc", SetLastError = true)] + private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset); + [DllImport("libc", SetLastError = true)] + private static extern int munmap(IntPtr addr, IntPtr length); + [DllImport("libc", SetLastError = true)] + private static extern long sysconf(int name); + + private bool? _useMmap; + private bool UseMmap + => _useMmap ?? ((_useMmap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)).Value); + + // Could be replaced with https://github.com/dotnet/runtime/issues/40892 when it will be available. + private IntPtr Alloc(int size) + { + if (!UseMmap) + { + return Marshal.AllocHGlobal(size); + } + else + { + var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero); + if (rv.ToInt64() == -1 || (ulong)rv.ToInt64() == 0xffffffff) + { + var errno = Marshal.GetLastWin32Error(); + throw new Exception("Unable to allocate memory: " + errno); + } + return rv; + } + } + + private void Free(IntPtr ptr, int len) + { + if (!UseMmap) + { + Marshal.FreeHGlobal(ptr); + } + else + { + if (munmap(ptr, new IntPtr(len)) == -1) + { + var errno = Marshal.GetLastWin32Error(); + throw new Exception("Unable to free memory: " + errno); + } + } + } +} diff --git a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs index 8124ce6bc4..d9db4b71d5 100644 --- a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs +++ b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform.Interop { + [Unstable] public interface IDynamicLibraryLoader { IntPtr LoadLibrary(string dll); @@ -14,5 +16,10 @@ namespace Avalonia.Platform.Interop { } + + public DynamicLibraryLoaderException(string message, Exception innerException) : base(message, innerException) + { + + } } } diff --git a/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs new file mode 100644 index 0000000000..4df9e8e917 --- /dev/null +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatform.cs @@ -0,0 +1,58 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Avalonia.Platform.Internal; + +namespace Avalonia.Platform +{ + public class StandardRuntimePlatform : IRuntimePlatform + { + public IDisposable StartSystemTimer(TimeSpan interval, Action tick) + { + return new Timer(_ => tick(), null, interval, interval); + } + + public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(size); + + private static readonly Lazy Info = new(() => + { + OperatingSystemType os; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + os = OperatingSystemType.OSX; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + os = OperatingSystemType.Linux; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + os = OperatingSystemType.WinNT; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Android"))) + os = OperatingSystemType.Android; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("iOS"))) + os = OperatingSystemType.iOS; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("Browser"))) + os = OperatingSystemType.Browser; + else + throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); + + // Source: https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs + var isCoreClr = Environment.Version.Major >= 5 || RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase); + var isMonoRuntime = Type.GetType("Mono.Runtime") != null; + var isFramework = !isCoreClr && RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); + + return new RuntimePlatformInfo + { + IsCoreClr = isCoreClr, + IsDotNetFramework = isFramework, + IsMono = isMonoRuntime, + + IsDesktop = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.WinNT, + IsMobile = os is OperatingSystemType.Android or OperatingSystemType.iOS, + IsUnix = os is OperatingSystemType.Linux or OperatingSystemType.OSX or OperatingSystemType.Android, + IsBrowser = os == OperatingSystemType.Browser, + OperatingSystem = os, + }; + }); + + + public virtual RuntimePlatformInfo GetRuntimeInfo() => Info.Value; + } +} diff --git a/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs similarity index 71% rename from src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs rename to src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs index ae7478feb9..65d6733399 100644 --- a/src/Avalonia.PlatformSupport/StandardRuntimePlatformServices.cs +++ b/src/Avalonia.Base/Platform/StandardRuntimePlatformServices.cs @@ -1,30 +1,33 @@ using System.Reflection; -using Avalonia.Platform; +using Avalonia.Platform.Internal; using Avalonia.Platform.Interop; -namespace Avalonia.PlatformSupport +namespace Avalonia.Platform { public static class StandardRuntimePlatformServices { public static void Register(Assembly? assembly = null) { var standardPlatform = new StandardRuntimePlatform(); - var os = standardPlatform.GetRuntimeInfo().OperatingSystem; AssetLoader.RegisterResUriParsers(); AvaloniaLocator.CurrentMutable .Bind().ToConstant(standardPlatform) .Bind().ToConstant(new AssetLoader(assembly)) .Bind().ToConstant( - os switch +#if NET6_0_OR_GREATER + new Net6Loader() +#else + standardPlatform.GetRuntimeInfo().OperatingSystem switch { - OperatingSystemType.WinNT => new Win32Loader(), + OperatingSystemType.WinNT => (IDynamicLibraryLoader)new Win32Loader(), OperatingSystemType.OSX => new UnixLoader(), OperatingSystemType.Linux => new UnixLoader(), OperatingSystemType.Android => new UnixLoader(), // iOS, WASM, ... - _ => (IDynamicLibraryLoader)new NotSupportedLoader() + _ => new NotSupportedLoader() } +#endif ); } } diff --git a/src/Avalonia.Base/Point.cs b/src/Avalonia.Base/Point.cs index 2f226caff4..0c789ff20f 100644 --- a/src/Avalonia.Base/Point.cs +++ b/src/Avalonia.Base/Point.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Numerics; #if !BUILDTASK using Avalonia.Animation.Animators; #endif @@ -168,12 +169,7 @@ namespace Avalonia /// The point. /// The matrix. /// The resulting point. - public static Point operator *(Point point, Matrix matrix) - { - return new Point( - (point.X * matrix.M11) + (point.Y * matrix.M21) + matrix.M31, - (point.X * matrix.M12) + (point.Y * matrix.M22) + matrix.M32); - } + public static Point operator *(Point point, Matrix matrix) => matrix.Transform(point); /// /// Parses a string. @@ -254,18 +250,7 @@ namespace Avalonia /// /// The transform. /// The transformed point. - public Point Transform(Matrix transform) - { - var x = X; - var y = Y; - var xadd = y * transform.M21 + transform.M31; - var yadd = x * transform.M12 + transform.M32; - x *= transform.M11; - x += xadd; - y *= transform.M22; - y += yadd; - return new Point(x, y); - } + public Point Transform(Matrix transform) => transform.Transform(this); /// /// Returns a new point with the specified X coordinate. diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 2c40c768f5..c8368e6d7a 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -26,7 +26,6 @@ using Avalonia.Metadata; [assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.PlatformSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] diff --git a/src/Avalonia.Base/PropertyStore/PriorityValue.cs b/src/Avalonia.Base/PropertyStore/PriorityValue.cs index 112cf6619f..182b2638c4 100644 --- a/src/Avalonia.Base/PropertyStore/PriorityValue.cs +++ b/src/Avalonia.Base/PropertyStore/PriorityValue.cs @@ -121,6 +121,7 @@ namespace Avalonia.PropertyStore public void ClearLocalValue() { + _localValue = default; UpdateEffectiveValue(new AvaloniaPropertyChangedEventArgs( _owner, Property, diff --git a/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs b/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs index f60ef247d5..f75917a00e 100644 --- a/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs +++ b/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs @@ -30,13 +30,15 @@ namespace Avalonia.Reactive } catch (InvalidCastException e) { + var unwrappedValue = value.HasValue ? value.Value : null; + Logger.TryGet(LogEventLevel.Error, LogArea.Binding)?.Log( _target, "Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})", _property.Name, _property.PropertyType, - value.Value, - value.Value?.GetType()); + unwrappedValue, + unwrappedValue?.GetType()); PublishNext(BindingValue.BindingError(e)); } } diff --git a/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs b/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs index eab3dca58e..1c6bd69158 100644 --- a/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs +++ b/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Rendering { + [Unstable] public interface IDeferredRendererLock { IDisposable? TryLock(); diff --git a/src/Avalonia.Base/Rendering/IRenderLoop.cs b/src/Avalonia.Base/Rendering/IRenderLoop.cs index dd7442e7f8..9838967261 100644 --- a/src/Avalonia.Base/Rendering/IRenderLoop.cs +++ b/src/Avalonia.Base/Rendering/IRenderLoop.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Rendering +using Avalonia.Metadata; + +namespace Avalonia.Rendering { /// /// The application render loop. @@ -7,6 +9,7 @@ /// The render loop is responsible for advancing the animation timer and updating the scene /// graph for visible windows. /// + [NotClientImplementable] public interface IRenderLoop { /// diff --git a/src/Avalonia.Base/Rendering/IRenderRoot.cs b/src/Avalonia.Base/Rendering/IRenderRoot.cs index 54e58bf39c..1aa44158b2 100644 --- a/src/Avalonia.Base/Rendering/IRenderRoot.cs +++ b/src/Avalonia.Base/Rendering/IRenderRoot.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.VisualTree; @@ -6,6 +7,7 @@ namespace Avalonia.Rendering /// /// Represents the root of a renderable tree. /// + [NotClientImplementable] public interface IRenderRoot : IVisual { /// diff --git a/src/Avalonia.Base/Rendering/IRenderTimer.cs b/src/Avalonia.Base/Rendering/IRenderTimer.cs index d333e928a0..ee74c345be 100644 --- a/src/Avalonia.Base/Rendering/IRenderTimer.cs +++ b/src/Avalonia.Base/Rendering/IRenderTimer.cs @@ -1,11 +1,13 @@ using System; using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Rendering { /// /// Defines the interface implemented by an application render timer. /// + [NotClientImplementable] public interface IRenderTimer { /// diff --git a/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs b/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs index 00449c5344..b5ab8ed0bd 100644 --- a/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs +++ b/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs @@ -1,4 +1,5 @@ using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Rendering { @@ -6,6 +7,7 @@ namespace Avalonia.Rendering /// Internal interface for initializing controls that are to be used as the visual in a /// . /// + [Unstable] public interface IVisualBrushInitialize { /// diff --git a/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs b/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs index 1cd6515635..f5312ad39b 100644 --- a/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs +++ b/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs @@ -1,4 +1,5 @@ using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Rendering @@ -6,6 +7,7 @@ namespace Avalonia.Rendering /// /// Defines a renderer used to render a visual brush to a bitmap. /// + [Unstable] public interface IVisualBrushRenderer { /// diff --git a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs index 2c0298affa..54b2ce5a25 100644 --- a/src/Avalonia.Base/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs @@ -277,18 +277,20 @@ namespace Avalonia.Rendering var m = Matrix.CreateTranslation(visual.Bounds.Position); var renderTransform = Matrix.Identity; + + // this should be calculated BEFORE renderTransform + if (visual.HasMirrorTransform) + { + var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0); + renderTransform *= mirrorMatrix; + } if (visual.RenderTransform != null) { var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); var offset = Matrix.CreateTranslation(origin); - renderTransform = (-offset) * visual.RenderTransform.Value * (offset); - } - - if (visual.HasMirrorTransform) - { - var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0); - renderTransform *= mirrorMatrix; + var finalTransform = (-offset) * visual.RenderTransform.Value * (offset); + renderTransform *= finalTransform; } m = renderTransform * m; diff --git a/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs index 019c3e0e9b..5dc426ab06 100644 --- a/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs @@ -188,19 +188,21 @@ namespace Avalonia.Rendering.SceneGraph var renderTransform = Matrix.Identity; - if (visual.RenderTransform != null) - { - var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); - var offset = Matrix.CreateTranslation(origin); - renderTransform = (-offset) * visual.RenderTransform.Value * (offset); - } - + // this should be calculated BEFORE renderTransform if (visual.HasMirrorTransform) { var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0); renderTransform *= mirrorMatrix; } + if (visual.RenderTransform != null) + { + var origin = visual.RenderTransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height)); + var offset = Matrix.CreateTranslation(origin); + var finalTransform = (-offset) * visual.RenderTransform.Value * (offset); + renderTransform *= finalTransform; + } + m = renderTransform * m; using (contextImpl.BeginUpdate(node)) diff --git a/src/Avalonia.Base/Rotate3DTransform.cs b/src/Avalonia.Base/Rotate3DTransform.cs new file mode 100644 index 0000000000..2c4e515861 --- /dev/null +++ b/src/Avalonia.Base/Rotate3DTransform.cs @@ -0,0 +1,210 @@ +using System; +using System.Numerics; +using Avalonia.Animation.Animators; + +namespace Avalonia.Media; + +/// +/// Non-Affine 3D transformation for rotating a visual around a definable axis +/// +public class Rotate3DTransform : Transform +{ + private readonly bool _isInitializing; + + /// + /// Defines the property. + /// + public static readonly StyledProperty AngleXProperty = + AvaloniaProperty.Register(nameof(AngleX)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty AngleYProperty = + AvaloniaProperty.Register(nameof(AngleY)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty AngleZProperty = + AvaloniaProperty.Register(nameof(AngleZ)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterXProperty = + AvaloniaProperty.Register(nameof(CenterX)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterYProperty = + AvaloniaProperty.Register(nameof(CenterY)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterZProperty = + AvaloniaProperty.Register(nameof(CenterZ)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty DepthProperty = + AvaloniaProperty.Register(nameof(Depth)); + + /// + /// Initializes a new instance of the class. + /// + public Rotate3DTransform() { } + + /// + /// Initializes a new instance of the class. + /// + /// The rotation around the X-Axis + /// The rotation around the Y-Axis + /// The rotation around the Z-Axis + /// The origin of the X-Axis + /// The origin of the Y-Axis + /// The origin of the Z-Axis + /// The depth of the 3D effect + public Rotate3DTransform( + double angleX, + double angleY, + double angleZ, + double centerX, + double centerY, + double centerZ, + double depth) : this() + { + _isInitializing = true; + AngleX = angleX; + AngleY = angleY; + AngleZ = angleZ; + CenterX = centerX; + CenterY = centerY; + CenterZ = centerZ; + Depth = depth; + _isInitializing = false; + } + + /// + /// Sets the rotation around the X-Axis + /// + public double AngleX + { + get => GetValue(AngleXProperty); + set => SetValue(AngleXProperty, value); + } + + /// + /// Sets the rotation around the Y-Axis + /// + public double AngleY + { + get => GetValue(AngleYProperty); + set => SetValue(AngleYProperty, value); + } + + /// + /// Sets the rotation around the Z-Axis + /// + public double AngleZ + { + get => GetValue(AngleZProperty); + set => SetValue(AngleZProperty, value); + } + + /// + /// Moves the origin the X-Axis rotates around + /// + public double CenterX + { + get => GetValue(CenterXProperty); + set => SetValue(CenterXProperty, value); + } + + /// + /// Moves the origin the Y-Axis rotates around + /// + public double CenterY + { + get => GetValue(CenterYProperty); + set => SetValue(CenterYProperty, value); + } + + /// + /// Moves the origin the Z-Axis rotates around + /// + public double CenterZ + { + get => GetValue(CenterZProperty); + set => SetValue(CenterZProperty, value); + } + + /// + /// Affects the depth of the rotation effect + /// + public double Depth + { + get => GetValue(DepthProperty); + set => SetValue(DepthProperty, value); + } + + /// + /// Gets the transform's . + /// + public override Matrix Value + { + get + { + var matrix44 = Matrix4x4.Identity; + //Copy values first, because it's not guaranteed, that values will not change during calculation + var (copyCenterX, + copyCenterY, + copyCenterZ, + copyAngleX, + copyAngleY, + copyAngleZ, + copyDepth) = (CenterX, CenterY, CenterZ, AngleX, AngleY, AngleZ, Depth); + + var centerSum = copyCenterX + copyCenterY + copyCenterZ; + + if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation(-(float)copyCenterX, -(float)copyCenterY, -(float)copyCenterZ); + + if (copyAngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(copyAngleX)); + if (copyAngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(copyAngleY)); + if (copyAngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(copyAngleZ)); + + if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation((float)copyCenterX, (float)copyCenterY, (float)copyCenterZ); + + if (copyDepth != 0) + { + var perspectiveMatrix = Matrix4x4.Identity; + perspectiveMatrix.M34 = -1 / (float)copyDepth; + matrix44 *= perspectiveMatrix; + } + + var matrix = new Matrix( + matrix44.M11, + matrix44.M12, + matrix44.M14, + matrix44.M21, + matrix44.M22, + matrix44.M24, + matrix44.M41, + matrix44.M42, + matrix44.M44); + + return matrix; + } + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + if (!_isInitializing) RaiseChanged(); + } +} diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs index dd5eb703ea..da607720ff 100644 --- a/src/Avalonia.Base/StyledPropertyBase.cs +++ b/src/Avalonia.Base/StyledPropertyBase.cs @@ -1,9 +1,7 @@ using System; -using System.Diagnostics; using Avalonia.Data; using Avalonia.Reactive; using Avalonia.Styling; -using Avalonia.Utilities; namespace Avalonia { @@ -12,7 +10,7 @@ namespace Avalonia /// public abstract class StyledPropertyBase : AvaloniaProperty, IStyledPropertyAccessor { - private bool _inherits; + private readonly bool _inherits; /// /// Initializes a new instance of the class. @@ -243,10 +241,10 @@ namespace Avalonia } else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) { - return new PropertySetterLazyInstance( + return new PropertySetterTemplateInstance( target, this, - () => (TValue)template.Build()); + template); } else { diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs index 479100ed8a..ac7b8b3ef1 100644 --- a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs +++ b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs @@ -1,6 +1,5 @@ -#nullable enable - -using System; +using System; +using Avalonia.Metadata; namespace Avalonia.Styling.Activators { @@ -16,6 +15,7 @@ namespace Avalonia.Styling.Activators /// - The subscription can have a tag associated with it, allowing a subscriber to index /// into a list of subscriptions without having to allocate additional objects. /// + [Unstable] public interface IStyleActivator : IDisposable { /// diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs index a1a6ef5c28..fbb18dc304 100644 --- a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs +++ b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs @@ -1,10 +1,11 @@ -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling.Activators { /// /// Receives notifications from an . /// + [Unstable] public interface IStyleActivatorSink { /// diff --git a/src/Avalonia.Base/Styling/ChildSelector.cs b/src/Avalonia.Base/Styling/ChildSelector.cs index 5c92182b80..34f3a76b61 100644 --- a/src/Avalonia.Base/Styling/ChildSelector.cs +++ b/src/Avalonia.Base/Styling/ChildSelector.cs @@ -37,13 +37,13 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var controlParent = ((ILogical)control).LogicalParent; if (controlParent != null) { - var parentMatch = _parent.Match((IStyleable)controlParent, subscribe); + var parentMatch = _parent.Match((IStyleable)controlParent, parent, subscribe); if (parentMatch.Result == SelectorMatchResult.Sometimes) { @@ -65,5 +65,6 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => null; + internal override bool HasValidNestingSelector() => _parent.HasValidNestingSelector(); } } diff --git a/src/Avalonia.Base/Styling/DescendentSelector.cs b/src/Avalonia.Base/Styling/DescendentSelector.cs index dde88b3436..4ffaff6861 100644 --- a/src/Avalonia.Base/Styling/DescendentSelector.cs +++ b/src/Avalonia.Base/Styling/DescendentSelector.cs @@ -35,7 +35,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var c = (ILogical)control; var descendantMatches = new OrActivatorBuilder(); @@ -46,7 +46,7 @@ namespace Avalonia.Styling if (c is IStyleable) { - var match = _parent.Match((IStyleable)c, subscribe); + var match = _parent.Match((IStyleable)c, parent, subscribe); if (match.Result == SelectorMatchResult.Sometimes) { @@ -70,5 +70,6 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => null; + internal override bool HasValidNestingSelector() => _parent.HasValidNestingSelector(); } } diff --git a/src/Avalonia.Base/Styling/IGlobalStyles.cs b/src/Avalonia.Base/Styling/IGlobalStyles.cs index ab24e3138c..f9667a8895 100644 --- a/src/Avalonia.Base/Styling/IGlobalStyles.cs +++ b/src/Avalonia.Base/Styling/IGlobalStyles.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Defines the style host that provides styles global to the application. /// + [NotClientImplementable] public interface IGlobalStyles : IStyleHost { /// diff --git a/src/Avalonia.Base/Styling/ISetter.cs b/src/Avalonia.Base/Styling/ISetter.cs index d588817be8..71ae5d84c0 100644 --- a/src/Avalonia.Base/Styling/ISetter.cs +++ b/src/Avalonia.Base/Styling/ISetter.cs @@ -1,12 +1,12 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Represents a setter for a . /// + [NotClientImplementable] public interface ISetter { /// diff --git a/src/Avalonia.Base/Styling/ISetterInstance.cs b/src/Avalonia.Base/Styling/ISetterInstance.cs index a299a87b64..e0d3137619 100644 --- a/src/Avalonia.Base/Styling/ISetterInstance.cs +++ b/src/Avalonia.Base/Styling/ISetterInstance.cs @@ -1,12 +1,12 @@ -#nullable enable - -using System; +using System; +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Represents a setter that has been instanced on a control. /// + [Unstable] public interface ISetterInstance : IDisposable { /// diff --git a/src/Avalonia.Base/Styling/IStyle.cs b/src/Avalonia.Base/Styling/IStyle.cs index 738a69cb88..e9faf82c07 100644 --- a/src/Avalonia.Base/Styling/IStyle.cs +++ b/src/Avalonia.Base/Styling/IStyle.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; using Avalonia.Controls; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Defines the interface for styles. /// + [NotClientImplementable] public interface IStyle : IResourceNode { /// diff --git a/src/Avalonia.Base/Styling/IStyleHost.cs b/src/Avalonia.Base/Styling/IStyleHost.cs index 360b40d9a1..2ae488825c 100644 --- a/src/Avalonia.Base/Styling/IStyleHost.cs +++ b/src/Avalonia.Base/Styling/IStyleHost.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; +using Avalonia.Metadata; #nullable enable @@ -8,6 +9,7 @@ namespace Avalonia.Styling /// /// Defines an element that has a collection. /// + [NotClientImplementable] public interface IStyleHost { /// diff --git a/src/Avalonia.Base/Styling/IStyleInstance.cs b/src/Avalonia.Base/Styling/IStyleInstance.cs index 8ddb989bc0..262f336e05 100644 --- a/src/Avalonia.Base/Styling/IStyleInstance.cs +++ b/src/Avalonia.Base/Styling/IStyleInstance.cs @@ -1,12 +1,12 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Represents a style that has been instanced on a control. /// + [Unstable] public interface IStyleInstance : IDisposable { /// @@ -14,6 +14,11 @@ namespace Avalonia.Styling /// IStyle Source { get; } + /// + /// Gets a value indicating whether this style has an activator. + /// + bool HasActivator { get; } + /// /// Gets a value indicating whether this style is active. /// diff --git a/src/Avalonia.Base/Styling/IStyleable.cs b/src/Avalonia.Base/Styling/IStyleable.cs index a3df779057..5bc972e7ab 100644 --- a/src/Avalonia.Base/Styling/IStyleable.cs +++ b/src/Avalonia.Base/Styling/IStyleable.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Collections; +using Avalonia.Metadata; #nullable enable @@ -9,6 +10,7 @@ namespace Avalonia.Styling /// /// Interface for styleable elements. /// + [NotClientImplementable] public interface IStyleable : IAvaloniaObject, INamed { /// diff --git a/src/Avalonia.Base/Styling/ITemplatedControl.cs b/src/Avalonia.Base/Styling/ITemplatedControl.cs index 5485babb62..68989c8a5e 100644 --- a/src/Avalonia.Base/Styling/ITemplatedControl.cs +++ b/src/Avalonia.Base/Styling/ITemplatedControl.cs @@ -1,6 +1,8 @@ +using Avalonia.Metadata; namespace Avalonia.Styling { + [NotClientImplementable] public interface ITemplatedControl : IAvaloniaObject { } diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs new file mode 100644 index 0000000000..481a937867 --- /dev/null +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -0,0 +1,30 @@ +using System; + +namespace Avalonia.Styling +{ + /// + /// The `^` nesting style selector. + /// + internal class NestingSelector : Selector + { + public override bool InTemplate => false; + public override bool IsCombinator => false; + public override Type? TargetType => null; + + public override string ToString() => "^"; + + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) + { + if (parent is Style s && s.Selector is Selector selector) + { + return selector.Match(control, (parent as Style)?.Parent, subscribe); + } + + throw new InvalidOperationException( + "Nesting selector was specified but cannot determine parent selector."); + } + + protected override Selector? MovePrevious() => null; + internal override bool HasValidNestingSelector() => true; + } +} diff --git a/src/Avalonia.Base/Styling/NotSelector.cs b/src/Avalonia.Base/Styling/NotSelector.cs index ab4e9d5d7f..cdc3254d38 100644 --- a/src/Avalonia.Base/Styling/NotSelector.cs +++ b/src/Avalonia.Base/Styling/NotSelector.cs @@ -45,9 +45,9 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { - var innerResult = _argument.Match(control, subscribe); + var innerResult = _argument.Match(control, parent, subscribe); switch (innerResult.Result) { @@ -67,5 +67,6 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => _previous; + internal override bool HasValidNestingSelector() => _argument.HasValidNestingSelector(); } } diff --git a/src/Avalonia.Base/Styling/NthChildSelector.cs b/src/Avalonia.Base/Styling/NthChildSelector.cs index aff34ea17c..047bf434da 100644 --- a/src/Avalonia.Base/Styling/NthChildSelector.cs +++ b/src/Avalonia.Base/Styling/NthChildSelector.cs @@ -48,7 +48,7 @@ namespace Avalonia.Styling public int Step { get; } public int Offset { get; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { if (!(control is ILogical logical)) { @@ -105,6 +105,7 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => _previous; + internal override bool HasValidNestingSelector() => _previous?.HasValidNestingSelector() ?? false; public override string ToString() { diff --git a/src/Avalonia.Base/Styling/OrSelector.cs b/src/Avalonia.Base/Styling/OrSelector.cs index 3d6db9b01e..913c27bf0c 100644 --- a/src/Avalonia.Base/Styling/OrSelector.cs +++ b/src/Avalonia.Base/Styling/OrSelector.cs @@ -65,14 +65,14 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var activators = new OrActivatorBuilder(); var neverThisInstance = false; foreach (var selector in _selectors) { - var match = selector.Match(control, subscribe); + var match = selector.Match(control, parent, subscribe); switch (match.Result) { @@ -104,6 +104,19 @@ namespace Avalonia.Styling protected override Selector? MovePrevious() => null; + internal override bool HasValidNestingSelector() + { + foreach (var selector in _selectors) + { + if (!selector.HasValidNestingSelector()) + { + return false; + } + } + + return true; + } + private Type? EvaluateTargetType() { Type? result = null; diff --git a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs index 1cd1a650ef..7a37daf087 100644 --- a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs +++ b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs @@ -74,7 +74,7 @@ namespace Avalonia.Styling } /// - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { if (subscribe) { @@ -90,6 +90,7 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => _previous; + internal override bool HasValidNestingSelector() => _previous?.HasValidNestingSelector() ?? false; internal static bool Compare(Type propertyType, object? propertyValue, object? value) { diff --git a/src/Avalonia.Base/Styling/PropertySetterInstance.cs b/src/Avalonia.Base/Styling/PropertySetterInstance.cs index 48f462d006..9028224cc1 100644 --- a/src/Avalonia.Base/Styling/PropertySetterInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterInstance.cs @@ -18,7 +18,7 @@ namespace Avalonia.Styling private readonly DirectPropertyBase? _directProperty; private readonly T _value; private IDisposable? _subscription; - private bool _isActive; + private State _state; public PropertySetterInstance( IStyleable target, @@ -40,11 +40,13 @@ namespace Avalonia.Styling _value = value; } + private bool IsActive => _state == State.Active; + public void Start(bool hasActivator) { if (hasActivator) { - if (_styledProperty is object) + if (_styledProperty is not null) { _subscription = _target.Bind(_styledProperty, this, BindingPriority.StyleTrigger); } @@ -55,44 +57,50 @@ namespace Avalonia.Styling } else { - if (_styledProperty is object) + var target = (AvaloniaObject) _target; + + if (_styledProperty is not null) { - _subscription = _target.SetValue(_styledProperty!, _value, BindingPriority.Style); + _subscription = target.SetValue(_styledProperty!, _value, BindingPriority.Style); } else { - _target.SetValue(_directProperty!, _value); + target.SetValue(_directProperty!, _value); } } } public void Activate() { - if (!_isActive) + if (!IsActive) { - _isActive = true; + _state = State.Active; PublishNext(); } } public void Deactivate() { - if (_isActive) + if (IsActive) { - _isActive = false; + _state = State.Inactive; PublishNext(); } } public override void Dispose() { + if (_state == State.Disposed) + return; + _state = State.Disposed; + if (_subscription is object) { var sub = _subscription; _subscription = null; sub.Dispose(); } - else if (_isActive) + else if (IsActive) { if (_styledProperty is object) { @@ -112,7 +120,14 @@ namespace Avalonia.Styling private void PublishNext() { - PublishNext(_isActive ? new BindingValue(_value) : default); + PublishNext(IsActive ? new BindingValue(_value) : default); + } + + private enum State + { + Inactive, + Active, + Disposed, } } } diff --git a/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs b/src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs similarity index 79% rename from src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs rename to src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs index 92653d0064..0f6efef1be 100644 --- a/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs @@ -11,42 +11,42 @@ namespace Avalonia.Styling /// evaluated. /// /// The target property type. - internal class PropertySetterLazyInstance : SingleSubscriberObservableBase>, + internal class PropertySetterTemplateInstance : SingleSubscriberObservableBase>, ISetterInstance { private readonly IStyleable _target; private readonly StyledPropertyBase? _styledProperty; private readonly DirectPropertyBase? _directProperty; - private readonly Func _valueFactory; + private readonly ITemplate _template; private BindingValue _value; private IDisposable? _subscription; private bool _isActive; - public PropertySetterLazyInstance( + public PropertySetterTemplateInstance( IStyleable target, StyledPropertyBase property, - Func valueFactory) + ITemplate template) { _target = target; _styledProperty = property; - _valueFactory = valueFactory; + _template = template; } - public PropertySetterLazyInstance( + public PropertySetterTemplateInstance( IStyleable target, DirectPropertyBase property, - Func valueFactory) + ITemplate template) { _target = target; _directProperty = property; - _valueFactory = valueFactory; + _template = template; } public void Start(bool hasActivator) { _isActive = !hasActivator; - if (_styledProperty is object) + if (_styledProperty is not null) { var priority = hasActivator ? BindingPriority.StyleTrigger : BindingPriority.Style; _subscription = _target.Bind(_styledProperty, this, priority); @@ -77,7 +77,7 @@ namespace Avalonia.Styling public override void Dispose() { - if (_subscription is object) + if (_subscription is not null) { var sub = _subscription; _subscription = null; @@ -85,7 +85,7 @@ namespace Avalonia.Styling } else if (_isActive) { - if (_styledProperty is object) + if (_styledProperty is not null) { _target.ClearValue(_styledProperty); } @@ -101,22 +101,21 @@ namespace Avalonia.Styling protected override void Subscribed() => PublishNext(); protected override void Unsubscribed() { } - private T GetValue() + private void EnsureTemplate() { if (_value.HasValue) { - return _value.Value; + return; } - _value = _valueFactory(); - return _value.Value; + _value = (T) _template.Build(); } private void PublishNext() { if (_isActive) { - GetValue(); + EnsureTemplate(); PublishNext(_value); } else diff --git a/src/Avalonia.Base/Styling/Selector.cs b/src/Avalonia.Base/Styling/Selector.cs index 0740e0f891..1e06f3d375 100644 --- a/src/Avalonia.Base/Styling/Selector.cs +++ b/src/Avalonia.Base/Styling/Selector.cs @@ -33,22 +33,25 @@ namespace Avalonia.Styling /// Tries to match the selector with a control. /// /// The control. + /// + /// The parent style, if the style containing the selector is a nested style. + /// /// /// Whether the match should subscribe to changes in order to track the match over time, /// or simply return an immediate result. /// /// A . - public SelectorMatch Match(IStyleable control, bool subscribe = true) + public SelectorMatch Match(IStyleable control, IStyle? parent = null, bool subscribe = true) { // First match the selector until a combinator is found. Selectors are stored from // right-to-left, so MatchUntilCombinator reverses this order because the type selector // will be on the left. - var match = MatchUntilCombinator(control, this, subscribe, out var combinator); + var match = MatchUntilCombinator(control, this, parent, subscribe, out var combinator); // If the pre-combinator selector matches, we can now match the combinator, if any. if (match.IsMatch && combinator is object) { - match = match.And(combinator.Match(control, subscribe)); + match = match.And(combinator.Match(control, parent, subscribe)); // If we have a combinator then we can never say that we always match a control of // this type, because by definition the combinator matches on things outside of the @@ -68,28 +71,34 @@ namespace Avalonia.Styling /// Evaluates the selector for a match. /// /// The control. + /// + /// The parent style, if the style containing the selector is a nested style. + /// /// /// Whether the match should subscribe to changes in order to track the match over time, /// or simply return an immediate result. /// /// A . - protected abstract SelectorMatch Evaluate(IStyleable control, bool subscribe); + protected abstract SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe); /// /// Moves to the previous selector. /// protected abstract Selector? MovePrevious(); + internal abstract bool HasValidNestingSelector(); + private static SelectorMatch MatchUntilCombinator( IStyleable control, Selector start, + IStyle? parent, bool subscribe, out Selector? combinator) { combinator = null; var activators = new AndActivatorBuilder(); - var result = Match(control, start, subscribe, ref activators, ref combinator); + var result = Match(control, start, parent, subscribe, ref activators, ref combinator); return result == SelectorMatchResult.Sometimes ? new SelectorMatch(activators.Get()) : @@ -99,6 +108,7 @@ namespace Avalonia.Styling private static SelectorMatchResult Match( IStyleable control, Selector selector, + IStyle? parent, bool subscribe, ref AndActivatorBuilder activators, ref Selector? combinator) @@ -110,7 +120,7 @@ namespace Avalonia.Styling // opportunity to exit early. if (previous != null && !previous.IsCombinator) { - var previousMatch = Match(control, previous, subscribe, ref activators, ref combinator); + var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator); if (previousMatch < SelectorMatchResult.Sometimes) { @@ -119,7 +129,7 @@ namespace Avalonia.Styling } // Match this selector. - var match = selector.Evaluate(control, subscribe); + var match = selector.Evaluate(control, parent, subscribe); if (!match.IsMatch) { diff --git a/src/Avalonia.Base/Styling/Selectors.cs b/src/Avalonia.Base/Styling/Selectors.cs index 7c66469cf1..476d86cd11 100644 --- a/src/Avalonia.Base/Styling/Selectors.cs +++ b/src/Avalonia.Base/Styling/Selectors.cs @@ -109,6 +109,11 @@ namespace Avalonia.Styling } } + public static Selector Nesting(this Selector? previous) + { + return new NestingSelector(); + } + /// /// Returns a selector which inverts the results of selector argument. /// diff --git a/src/Avalonia.Base/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs index b4b3399022..d989bb0706 100644 --- a/src/Avalonia.Base/Styling/Setter.cs +++ b/src/Avalonia.Base/Styling/Setter.cs @@ -1,9 +1,7 @@ using System; using Avalonia.Animation; using Avalonia.Data; -using Avalonia.Data.Core; using Avalonia.Metadata; -using Avalonia.Utilities; #nullable enable @@ -70,12 +68,5 @@ namespace Avalonia.Styling return Property.CreateSetterInstance(target, Value); } - - private struct SetterVisitorData - { - public IStyleable target; - public object? value; - public ISetterInstance? result; - } } } diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index 00819ef7be..000e588bad 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -4,8 +4,6 @@ using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Metadata; -#nullable enable - namespace Avalonia.Styling { /// @@ -14,9 +12,11 @@ namespace Avalonia.Styling public class Style : AvaloniaObject, IStyle, IResourceProvider { private IResourceHost? _owner; + private StyleChildren? _children; private IResourceDictionary? _resources; private List? _setters; private List? _animations; + private StyleCache? _childCache; /// /// Initializes a new instance of the class. @@ -34,6 +34,14 @@ namespace Avalonia.Styling Selector = selector(null); } + /// + /// Gets the children of the style. + /// + public IList Children => _children ??= new(this); + + /// + /// Gets the or Application that hosts the style. + /// public IResourceHost? Owner { get => _owner; @@ -47,6 +55,11 @@ namespace Avalonia.Styling } } + /// + /// Gets the parent style if this style is hosted in a collection. + /// + public Style? Parent { get; private set; } + /// /// Gets or sets a dictionary of style resources. /// @@ -81,7 +94,6 @@ namespace Avalonia.Styling /// /// Gets the style's setters. /// - [Content] public IList Setters => _setters ??= new List(); /// @@ -90,15 +102,18 @@ namespace Avalonia.Styling public IList Animations => _animations ??= new List(); bool IResourceNode.HasResources => _resources?.Count > 0; - IReadOnlyList IStyle.Children => Array.Empty(); + IReadOnlyList IStyle.Children => (IReadOnlyList?)_children ?? Array.Empty(); public event EventHandler? OwnerChanged; + public void Add(ISetter setter) => Setters.Add(setter); + public void Add(IStyle style) => Children.Add(style); + public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) { target = target ?? throw new ArgumentNullException(nameof(target)); - var match = Selector is object ? Selector.Match(target) : + var match = Selector is object ? Selector.Match(target, Parent) : target == host ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; if (match.IsMatch && (_setters is object || _animations is object)) @@ -108,7 +123,17 @@ namespace Avalonia.Styling instance.Start(); } - return match.Result; + var result = match.Result; + + if (_children is not null) + { + _childCache ??= new StyleCache(); + var childResult = _childCache.TryAttach(_children, target, host); + if (childResult > result) + result = childResult; + } + + return result; } public bool TryGetResource(object key, out object? result) @@ -156,5 +181,18 @@ namespace Avalonia.Styling _resources?.RemoveOwner(owner); } } + + internal void SetParent(Style? parent) + { + if (parent?.Selector is not null) + { + if (Selector is null) + throw new InvalidOperationException("Child styles must have a selector."); + if (!Selector.HasValidNestingSelector()) + throw new InvalidOperationException("Child styles must have a nesting selector."); + } + + Parent = parent; + } } } diff --git a/src/Avalonia.Base/Styling/StyleCache.cs b/src/Avalonia.Base/Styling/StyleCache.cs new file mode 100644 index 0000000000..3285476880 --- /dev/null +++ b/src/Avalonia.Base/Styling/StyleCache.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; + +namespace Avalonia.Styling +{ + /// + /// Simple cache for improving performance of applying styles. + /// + /// + /// Maps to a list of styles that are known be be possible + /// matches. + /// + internal class StyleCache : Dictionary?> + { + public SelectorMatchResult TryAttach(IList styles, IStyleable target, IStyleHost? host) + { + if (TryGetValue(target.StyleKey, out var cached)) + { + if (cached is object) + { + var result = SelectorMatchResult.NeverThisType; + + foreach (var style in cached) + { + var childResult = style.TryAttach(target, host); + if (childResult > result) + result = childResult; + } + + return result; + } + else + { + return SelectorMatchResult.NeverThisType; + } + } + else + { + List? matches = null; + + foreach (var child in styles) + { + if (child.TryAttach(target, host) != SelectorMatchResult.NeverThisType) + { + matches ??= new List(); + matches.Add(child); + } + } + + Add(target.StyleKey, matches); + + return matches is null ? + SelectorMatchResult.NeverThisType : + SelectorMatchResult.AlwaysThisType; + } + } + } +} diff --git a/src/Avalonia.Base/Styling/StyleChildren.cs b/src/Avalonia.Base/Styling/StyleChildren.cs new file mode 100644 index 0000000000..5f8635f155 --- /dev/null +++ b/src/Avalonia.Base/Styling/StyleChildren.cs @@ -0,0 +1,35 @@ +using System.Collections.ObjectModel; +using Avalonia.Controls; + +namespace Avalonia.Styling +{ + internal class StyleChildren : Collection + { + private readonly Style _owner; + + public StyleChildren(Style owner) => _owner = owner; + + protected override void InsertItem(int index, IStyle item) + { + (item as Style)?.SetParent(_owner); + base.InsertItem(index, item); + } + + protected override void RemoveItem(int index) + { + var item = Items[index]; + (item as Style)?.SetParent(null); + if (_owner.Owner is IResourceHost host) + (item as IResourceProvider)?.RemoveOwner(host); + base.RemoveItem(index); + } + + protected override void SetItem(int index, IStyle item) + { + (item as Style)?.SetParent(_owner); + base.SetItem(index, item); + if (_owner.Owner is IResourceHost host) + (item as IResourceProvider)?.AddOwner(host); + } + } +} diff --git a/src/Avalonia.Base/Styling/StyleInstance.cs b/src/Avalonia.Base/Styling/StyleInstance.cs index 830cf49a0d..db96da6821 100644 --- a/src/Avalonia.Base/Styling/StyleInstance.cs +++ b/src/Avalonia.Base/Styling/StyleInstance.cs @@ -11,10 +11,10 @@ namespace Avalonia.Styling /// /// A which has been instanced on a control. /// - internal class StyleInstance : IStyleInstance, IStyleActivatorSink + internal sealed class StyleInstance : IStyleInstance, IStyleActivatorSink { - private readonly List? _setters; - private readonly List? _animations; + private readonly ISetterInstance[]? _setters; + private readonly IDisposable[]? _animations; private readonly IStyleActivator? _activator; private readonly Subject? _animationTrigger; @@ -30,41 +30,42 @@ namespace Avalonia.Styling _activator = activator; IsActive = _activator is null; - if (setters is object) + if (setters is not null) { var setterCount = setters.Count; - _setters = new List(setterCount); + _setters = new ISetterInstance[setterCount]; for (var i = 0; i < setterCount; ++i) { - _setters.Add(setters[i].Instance(Target)); + _setters[i] = setters[i].Instance(Target); } } - if (animations is object && target is Animatable animatable) + if (animations is not null && target is Animatable animatable) { var animationsCount = animations.Count; - _animations = new List(animationsCount); + _animations = new IDisposable[animationsCount]; _animationTrigger = new Subject(); for (var i = 0; i < animationsCount; ++i) { - _animations.Add(animations[i].Apply(animatable, null, _animationTrigger)); + _animations[i] = animations[i].Apply(animatable, null, _animationTrigger); } } } + public bool HasActivator => _activator is not null; public bool IsActive { get; private set; } public IStyle Source { get; } public IStyleable Target { get; } public void Start() { - var hasActivator = _activator is object; + var hasActivator = HasActivator; - if (_setters is object) + if (_setters is not null) { foreach (var setter in _setters) { @@ -76,7 +77,7 @@ namespace Avalonia.Styling { _activator!.Subscribe(this, 0); } - else if (_animationTrigger != null) + else if (_animationTrigger is not null) { _animationTrigger.OnNext(true); } @@ -84,7 +85,7 @@ namespace Avalonia.Styling public void Dispose() { - if (_setters is object) + if (_setters is not null) { foreach (var setter in _setters) { @@ -92,11 +93,11 @@ namespace Avalonia.Styling } } - if (_animations is object) + if (_animations is not null) { - foreach (var subscripion in _animations) + foreach (var subscription in _animations) { - subscripion.Dispose(); + subscription.Dispose(); } } @@ -111,7 +112,7 @@ namespace Avalonia.Styling _animationTrigger?.OnNext(value); - if (_setters is object) + if (_setters is not null) { if (IsActive) { diff --git a/src/Avalonia.Base/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs index d79081152e..e4c3371007 100644 --- a/src/Avalonia.Base/Styling/Styles.cs +++ b/src/Avalonia.Base/Styling/Styles.cs @@ -17,10 +17,10 @@ namespace Avalonia.Styling IStyle, IResourceProvider { - private readonly AvaloniaList _styles = new AvaloniaList(); + private readonly AvaloniaList _styles = new(); private IResourceHost? _owner; private IResourceDictionary? _resources; - private Dictionary?>? _cache; + private StyleCache? _cache; public Styles() { @@ -62,16 +62,18 @@ namespace Avalonia.Styling { value = value ?? throw new ArgumentNullException(nameof(Resources)); - if (Owner is object) + var currentOwner = Owner; + + if (currentOwner is not null) { - _resources?.RemoveOwner(Owner); + _resources?.RemoveOwner(currentOwner); } _resources = value; - if (Owner is object) + if (currentOwner is not null) { - _resources.AddOwner(Owner); + _resources.AddOwner(currentOwner); } } } @@ -89,7 +91,7 @@ namespace Avalonia.Styling foreach (var i in this) { - if (i is IResourceProvider p && p.HasResources) + if (i is IResourceProvider { HasResources: true }) { return true; } @@ -111,43 +113,8 @@ namespace Avalonia.Styling public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) { - _cache ??= new Dictionary?>(); - - if (_cache.TryGetValue(target.StyleKey, out var cached)) - { - if (cached is object) - { - foreach (var style in cached) - { - style.TryAttach(target, host); - } - - return SelectorMatchResult.AlwaysThisType; - } - else - { - return SelectorMatchResult.NeverThisType; - } - } - else - { - List? matches = null; - - foreach (var child in this) - { - if (child.TryAttach(target, host) != SelectorMatchResult.NeverThisType) - { - matches ??= new List(); - matches.Add(child); - } - } - - _cache.Add(target.StyleKey, matches); - - return matches is null ? - SelectorMatchResult.NeverThisType : - SelectorMatchResult.AlwaysThisType; - } + _cache ??= new StyleCache(); + return _cache.TryAttach(this, target, host); } /// @@ -225,7 +192,7 @@ namespace Avalonia.Styling { owner = owner ?? throw new ArgumentNullException(nameof(owner)); - if (Owner != null) + if (Owner is not null) { throw new InvalidOperationException("The Styles already has a owner."); } @@ -262,70 +229,81 @@ namespace Avalonia.Styling } } - private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + private static IReadOnlyList ToReadOnlyList(ICollection list) { - static IReadOnlyList ToReadOnlyList(IList list) + if (list is IReadOnlyList readOnlyList) { - if (list is IReadOnlyList) - { - return (IReadOnlyList)list; - } - else - { - var result = new T[list.Count]; - list.CopyTo(result, 0); - return result; - } + return readOnlyList; } - void Add(IList items) + var result = new T[list.Count]; + list.CopyTo(result, 0); + return result; + } + + private static void InternalAdd(IList items, IResourceHost? owner, ref StyleCache? cache) + { + if (owner is not null) { for (var i = 0; i < items.Count; ++i) { - var style = (IStyle)items[i]!; - - if (Owner is object && style is IResourceProvider resourceProvider) + if (items[i] is IResourceProvider provider) { - resourceProvider.AddOwner(Owner); + provider.AddOwner(owner); } - - _cache = null; } - (Owner as IStyleHost)?.StylesAdded(ToReadOnlyList(items)); + (owner as IStyleHost)?.StylesAdded(ToReadOnlyList(items)); } - void Remove(IList items) + if (items.Count > 0) + { + cache = null; + } + } + + private static void InternalRemove(IList items, IResourceHost? owner, ref StyleCache? cache) + { + if (owner is not null) { for (var i = 0; i < items.Count; ++i) { - var style = (IStyle)items[i]!; - - if (Owner is object && style is IResourceProvider resourceProvider) + if (items[i] is IResourceProvider provider) { - resourceProvider.RemoveOwner(Owner); + provider.RemoveOwner(owner); } - - _cache = null; } - (Owner as IStyleHost)?.StylesRemoved(ToReadOnlyList(items)); + (owner as IStyleHost)?.StylesRemoved(ToReadOnlyList(items)); + } + + if (items.Count > 0) + { + cache = null; } + } + + private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Reset) + { + throw new InvalidOperationException("Reset should not be called on Styles."); + } + + var currentOwner = Owner; switch (e.Action) { case NotifyCollectionChangedAction.Add: - Add(e.NewItems!); + InternalAdd(e.NewItems!, currentOwner, ref _cache); break; case NotifyCollectionChangedAction.Remove: - Remove(e.OldItems!); + InternalRemove(e.OldItems!, currentOwner, ref _cache); break; case NotifyCollectionChangedAction.Replace: - Remove(e.OldItems!); - Add(e.NewItems!); + InternalRemove(e.OldItems!, currentOwner, ref _cache); + InternalAdd(e.NewItems!, currentOwner, ref _cache); break; - case NotifyCollectionChangedAction.Reset: - throw new InvalidOperationException("Reset should not be called on Styles."); } CollectionChanged?.Invoke(this, e); diff --git a/src/Avalonia.Base/Styling/TemplateSelector.cs b/src/Avalonia.Base/Styling/TemplateSelector.cs index e8051efa6d..b0a2dae8d6 100644 --- a/src/Avalonia.Base/Styling/TemplateSelector.cs +++ b/src/Avalonia.Base/Styling/TemplateSelector.cs @@ -36,7 +36,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var templatedParent = control.TemplatedParent as IStyleable; @@ -45,9 +45,10 @@ namespace Avalonia.Styling return SelectorMatch.NeverThisInstance; } - return _parent.Match(templatedParent, subscribe); + return _parent.Match(templatedParent, parent, subscribe); } protected override Selector? MovePrevious() => null; + internal override bool HasValidNestingSelector() => _parent?.HasValidNestingSelector() ?? false; } } diff --git a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs index ef48c4a8cd..24d5d6bbbf 100644 --- a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs +++ b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs @@ -94,7 +94,7 @@ namespace Avalonia.Styling } /// - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { if (TargetType != null) { @@ -140,6 +140,7 @@ namespace Avalonia.Styling } protected override Selector? MovePrevious() => _previous; + internal override bool HasValidNestingSelector() => _previous?.HasValidNestingSelector() ?? false; private string BuildSelectorString() { diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs index 21649306cb..2cade55f32 100644 --- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs +++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs @@ -30,7 +30,7 @@ namespace Avalonia.Threading /// public override void Post(SendOrPostCallback d, object? state) { - Dispatcher.UIThread.Post(() => d(state), DispatcherPriority.Background); + Dispatcher.UIThread.Post(d, state, DispatcherPriority.Background); } /// diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs index 2eb2e7c01f..571f782813 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.cs @@ -118,7 +118,7 @@ namespace Avalonia.Threading } /// - public void Post(Action action, T arg, DispatcherPriority priority = default) + public void Post(SendOrPostCallback action, object? arg, DispatcherPriority priority = default) { _ = action ?? throw new ArgumentNullException(nameof(action)); _jobRunner.Post(action, arg, priority); diff --git a/src/Avalonia.Base/Threading/IDispatcher.cs b/src/Avalonia.Base/Threading/IDispatcher.cs index 713a7ac4d7..1c700132b7 100644 --- a/src/Avalonia.Base/Threading/IDispatcher.cs +++ b/src/Avalonia.Base/Threading/IDispatcher.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Avalonia.Threading @@ -26,6 +27,14 @@ namespace Avalonia.Threading /// The priority with which to invoke the method. void Post(Action action, DispatcherPriority priority = default); + /// + /// Posts an action that will be invoked on the dispatcher thread. + /// + /// The method. + /// The argument of method to call. + /// The priority with which to invoke the method. + void Post(SendOrPostCallback action, object? arg, DispatcherPriority priority = default); + /// /// Invokes a action on the dispatcher thread. /// diff --git a/src/Avalonia.Base/Threading/JobRunner.cs b/src/Avalonia.Base/Threading/JobRunner.cs index 4b304d44f6..ced3423c27 100644 --- a/src/Avalonia.Base/Threading/JobRunner.cs +++ b/src/Avalonia.Base/Threading/JobRunner.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Avalonia.Platform; @@ -81,9 +82,9 @@ namespace Avalonia.Threading /// The method to call. /// The parameter of method to call. /// The priority with which to invoke the method. - internal void Post(Action action, T parameter, DispatcherPriority priority) + internal void Post(SendOrPostCallback action, object? parameter, DispatcherPriority priority) { - AddJob(new Job(action, parameter, priority, true)); + AddJob(new JobWithArg(action, parameter, priority, true)); } /// @@ -207,11 +208,10 @@ namespace Avalonia.Threading /// /// A typed job to run. /// - /// Type of job parameter - private sealed class Job : IJob + private sealed class JobWithArg : IJob { - private readonly Action _action; - private readonly T _parameter; + private readonly SendOrPostCallback _action; + private readonly object? _parameter; private readonly TaskCompletionSource? _taskCompletionSource; /// @@ -222,7 +222,7 @@ namespace Avalonia.Threading /// The job priority. /// Do not wrap exception in TaskCompletionSource - public Job(Action action, T parameter, DispatcherPriority priority, bool throwOnUiThread) + public JobWithArg(SendOrPostCallback action, object? parameter, DispatcherPriority priority, bool throwOnUiThread) { _action = action; _parameter = parameter; diff --git a/src/Avalonia.Base/Utilities/WeakEvents.cs b/src/Avalonia.Base/Utilities/WeakEvents.cs index d1b5e7f12d..6da899bab2 100644 --- a/src/Avalonia.Base/Utilities/WeakEvents.cs +++ b/src/Avalonia.Base/Utilities/WeakEvents.cs @@ -31,10 +31,23 @@ public class WeakEvents return () => s.PropertyChanged -= handler; }); + + /// + /// Represents PropertyChanged event from + /// + public static readonly WeakEvent + AvaloniaPropertyChanged = WeakEvent.Register( + (s, h) => + { + EventHandler handler = (_, e) => h(s, e); + s.PropertyChanged += handler; + return () => s.PropertyChanged -= handler; + }); + /// /// Represents CanExecuteChanged event from /// public static readonly WeakEvent CommandCanExecuteChanged = WeakEvent.Register((s, h) => s.CanExecuteChanged += h, (s, h) => s.CanExecuteChanged -= h); -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index 4fd21f02f9..bdf8723b81 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -376,7 +376,9 @@ namespace Avalonia if (e.OldValue is IAffectsRender oldValue) { if (sender._affectsRenderWeakSubscriber != null) + { InvalidatedWeakEvent.Unsubscribe(oldValue, sender._affectsRenderWeakSubscriber); + } } if (e.NewValue is IAffectsRender newValue) diff --git a/src/Avalonia.Base/VisualExtensions.cs b/src/Avalonia.Base/VisualExtensions.cs index 3a3c2693d0..7a5bbb105c 100644 --- a/src/Avalonia.Base/VisualExtensions.cs +++ b/src/Avalonia.Base/VisualExtensions.cs @@ -101,6 +101,13 @@ namespace Avalonia while (v != ancestor) { + // this should be calculated BEFORE renderTransform + if (v.HasMirrorTransform) + { + var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, v.Bounds.Width, 0); + result *= mirrorMatrix; + } + if (v.RenderTransform?.Value != null) { var origin = v.RenderTransformOrigin.ToPixels(v.Bounds.Size); @@ -110,12 +117,6 @@ namespace Avalonia result *= renderTransform; } - if (v.HasMirrorTransform) - { - var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, v.Bounds.Width, 0); - result *= mirrorMatrix; - } - var topLeft = v.Bounds.TopLeft; if (topLeft != default) diff --git a/src/Avalonia.Base/VisualTree/IVisual.cs b/src/Avalonia.Base/VisualTree/IVisual.cs index b1251618c4..3b053fab38 100644 --- a/src/Avalonia.Base/VisualTree/IVisual.cs +++ b/src/Avalonia.Base/VisualTree/IVisual.cs @@ -1,10 +1,9 @@ using System; using Avalonia.Collections; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Rendering; -#nullable enable - namespace Avalonia.VisualTree { /// @@ -18,6 +17,7 @@ namespace Avalonia.VisualTree /// implemented by . It should not be necessary to implement it /// anywhere else. /// + [NotClientImplementable] public interface IVisual { /// diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index e9b99c9aa8..a801d338c3 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -103,5 +103,6 @@ + diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index fa437de186..b39ceab1b6 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -49,9 +49,9 @@ namespace Avalonia.Build.Tasks string projectDirectory, string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation, bool debuggerLaunch) { - var typeSystem = new CecilTypeSystem(references - .Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")) - .Concat(new[] { input }), input); + var typeSystem = new CecilTypeSystem( + references.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")), + input); var asm = typeSystem.TargetAssemblyDefinition; diff --git a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj index 0952c899d4..e0790795c5 100644 --- a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj +++ b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj @@ -22,4 +22,9 @@ + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 245592207e..7e6b70a146 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -128,8 +128,8 @@ namespace Avalonia.Controls.Primitives if (_inputTarget != null) { - _inputTarget.PointerEnter += InputTarget_PointerEnter; - _inputTarget.PointerLeave += InputTarget_PointerLeave; + _inputTarget.PointerEntered += InputTarget_PointerEntered; + _inputTarget.PointerExited += InputTarget_PointerExited; _inputTarget.PointerPressed += InputTarget_PointerPressed; _inputTarget.PointerMoved += InputTarget_PointerMoved; _inputTarget.PointerReleased += InputTarget_PointerReleased; @@ -194,8 +194,8 @@ namespace Avalonia.Controls.Primitives if (_inputTarget != null) { - _inputTarget.PointerEnter -= InputTarget_PointerEnter; - _inputTarget.PointerLeave -= InputTarget_PointerLeave; + _inputTarget.PointerEntered -= InputTarget_PointerEntered; + _inputTarget.PointerExited -= InputTarget_PointerExited; _inputTarget.PointerPressed -= InputTarget_PointerPressed; _inputTarget.PointerMoved -= InputTarget_PointerMoved; _inputTarget.PointerReleased -= InputTarget_PointerReleased; @@ -362,7 +362,7 @@ namespace Avalonia.Controls.Primitives } /// - protected override void OnPointerLeave(PointerEventArgs e) + protected override void OnPointerExited(PointerEventArgs e) { // We only want to bother with the color name tool tip if we can provide color names. if (_selectionEllipsePanel != null && @@ -373,7 +373,7 @@ namespace Avalonia.Controls.Primitives UpdatePseudoClasses(); - base.OnPointerLeave(e); + base.OnPointerExited(e); } /// @@ -848,16 +848,16 @@ namespace Avalonia.Controls.Primitives UpdatePseudoClasses(); } - /// - private void InputTarget_PointerEnter(object? sender, PointerEventArgs args) + /// + private void InputTarget_PointerEntered(object? sender, PointerEventArgs args) { _isPointerOver = true; UpdatePseudoClasses(); args.Handled = true; } - /// - private void InputTarget_PointerLeave(object? sender, PointerEventArgs args) + /// + private void InputTarget_PointerExited(object? sender, PointerEventArgs args) { _isPointerOver = false; UpdatePseudoClasses(); diff --git a/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs b/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs index 0135541349..64769303d6 100644 --- a/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs @@ -1,8 +1,5 @@ -using System.Runtime.CompilerServices; using Avalonia.Metadata; -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Collections")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj index 410cac72fc..6369961f0f 100644 --- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj +++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj @@ -18,4 +18,9 @@ + + + + + diff --git a/src/Avalonia.Controls.DataGrid/DataGridCell.cs b/src/Avalonia.Controls.DataGrid/DataGridCell.cs index 67183781d3..2a51f45896 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridCell.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridCell.cs @@ -139,18 +139,18 @@ namespace Avalonia.Controls } } - protected override void OnPointerEnter(PointerEventArgs e) + protected override void OnPointerEntered(PointerEventArgs e) { - base.OnPointerEnter(e); + base.OnPointerEntered(e); if (OwningRow != null) { IsMouseOver = true; } } - protected override void OnPointerLeave(PointerEventArgs e) + protected override void OnPointerExited(PointerEventArgs e) { - base.OnPointerLeave(e); + base.OnPointerExited(e); if (OwningRow != null) { diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs index 915b36687c..d3bd968d62 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs @@ -83,9 +83,9 @@ namespace Avalonia.Controls { PointerPressed += DataGridColumnHeader_PointerPressed; PointerReleased += DataGridColumnHeader_PointerReleased; - PointerMoved += DataGridColumnHeader_PointerMove; - PointerEnter += DataGridColumnHeader_PointerEnter; - PointerLeave += DataGridColumnHeader_PointerLeave; + PointerMoved += DataGridColumnHeader_PointerMoved; + PointerEntered += DataGridColumnHeader_PointerEntered; + PointerExited += DataGridColumnHeader_PointerExited; } private void OnAreSeparatorsVisibleChanged(AvaloniaPropertyChangedEventArgs e) @@ -452,7 +452,7 @@ namespace Avalonia.Controls SetDragCursor(mousePosition); } - private void DataGridColumnHeader_PointerEnter(object sender, PointerEventArgs e) + private void DataGridColumnHeader_PointerEntered(object sender, PointerEventArgs e) { if (!IsEnabled) { @@ -464,7 +464,7 @@ namespace Avalonia.Controls UpdatePseudoClasses(); } - private void DataGridColumnHeader_PointerLeave(object sender, PointerEventArgs e) + private void DataGridColumnHeader_PointerExited(object sender, PointerEventArgs e) { if (!IsEnabled) { @@ -506,7 +506,7 @@ namespace Avalonia.Controls UpdatePseudoClasses(); } - private void DataGridColumnHeader_PointerMove(object sender, PointerEventArgs e) + private void DataGridColumnHeader_PointerMoved(object sender, PointerEventArgs e) { if (OwningGrid == null || !IsEnabled) { diff --git a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs index a3095ad214..ae52e5f970 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs @@ -193,8 +193,11 @@ namespace Avalonia.Controls } } + /// Try get number of DataSource itmes. /// When "allowSlow" is false, method will not use Linq.Count() method and will return 0 or 1 instead. /// If "getAny" is true, method can use Linq.Any() method to speedup. + /// number of DataSource itmes. + /// true if able to retrieve number of DataSource itmes; otherwise, false. internal bool TryGetCount(bool allowSlow, bool getAny, out int count) { bool result; diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs index b062a14e39..1559763a1b 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs @@ -607,15 +607,15 @@ namespace Avalonia.Controls } } - protected override void OnPointerEnter(PointerEventArgs e) + protected override void OnPointerEntered(PointerEventArgs e) { - base.OnPointerEnter(e); + base.OnPointerEntered(e); IsMouseOver = true; } - protected override void OnPointerLeave(PointerEventArgs e) + protected override void OnPointerExited(PointerEventArgs e) { IsMouseOver = false; - base.OnPointerLeave(e); + base.OnPointerExited(e); } internal void ApplyCellsState() diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs index a3dfa44fc9..9f37e8a6aa 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs @@ -375,7 +375,7 @@ namespace Avalonia.Controls ApplyHeaderStatus(); } - protected override void OnPointerEnter(PointerEventArgs e) + protected override void OnPointerEntered(PointerEventArgs e) { if (IsEnabled) { @@ -383,10 +383,10 @@ namespace Avalonia.Controls UpdatePseudoClasses(); } - base.OnPointerEnter(e); + base.OnPointerEntered(e); } - protected override void OnPointerLeave(PointerEventArgs e) + protected override void OnPointerExited(PointerEventArgs e) { if (IsEnabled) { @@ -394,7 +394,7 @@ namespace Avalonia.Controls UpdatePseudoClasses(); } - base.OnPointerLeave(e); + base.OnPointerExited(e); } private void SetIsCheckedNoCallBack(bool value) diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs index 03299bbf35..fe3ba0abf6 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs @@ -158,23 +158,23 @@ namespace Avalonia.Controls.Primitives } } - protected override void OnPointerEnter(PointerEventArgs e) + protected override void OnPointerEntered(PointerEventArgs e) { if (OwningRow != null) { OwningRow.IsMouseOver = true; } - base.OnPointerEnter(e); + base.OnPointerEntered(e); } - protected override void OnPointerLeave(PointerEventArgs e) + protected override void OnPointerExited(PointerEventArgs e) { if (OwningRow != null) { OwningRow.IsMouseOver = false; } - base.OnPointerLeave(e); + base.OnPointerExited(e); } //TODO TabStop diff --git a/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs b/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs index e07c933039..38d559a031 100644 --- a/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs +++ b/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs @@ -218,6 +218,8 @@ namespace Avalonia.Controls.Primitives { // No explicit height values were set so we can autosize autoSizeHeight = true; + // We need to invalidate desired height in order to grow or shrink as needed + InvalidateDesiredHeight(); measureHeight = double.PositiveInfinity; } else diff --git a/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs b/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs index d61c05ab6e..64769303d6 100644 --- a/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs @@ -1,9 +1,5 @@ -using System.Runtime.CompilerServices; using Avalonia.Metadata; -[assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Collections")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml index 8243d9c22d..2baa8c88c9 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml @@ -1,87 +1,51 @@ - + - 12,0,12,0 - 0.6 0.8 + 12,0,12,0 M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z M1965 947l-941 -941l-941 941l90 90l787 -787v1798h128v-1798l787 787z M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z M1939 1581l90 -90l-1005 -1005l-1005 1005l90 90l915 -915z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Opacity="0.4" + Color="{DynamicResource SystemBaseMediumLowColor}" /> + + + diff --git a/src/Avalonia.PlatformSupport/AppBuilder.cs b/src/Avalonia.Controls/AppBuilder.cs similarity index 94% rename from src/Avalonia.PlatformSupport/AppBuilder.cs rename to src/Avalonia.Controls/AppBuilder.cs index 136f1f39b3..5bcd87162e 100644 --- a/src/Avalonia.PlatformSupport/AppBuilder.cs +++ b/src/Avalonia.Controls/AppBuilder.cs @@ -1,5 +1,5 @@ using Avalonia.Controls; -using Avalonia.PlatformSupport; +using Avalonia.Platform; namespace Avalonia { diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 8779ae9122..6b7101cd49 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Reflection; using System.Linq; using Avalonia.Controls.ApplicationLifetimes; @@ -120,38 +119,8 @@ namespace Avalonia.Controls return Self; } - /// - /// Starts the application with an instance of . - /// - /// The window type. - /// A delegate that will be called to create a data context for the window (optional). - [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details")] - public void Start(Func? dataContextProvider = null) - where TMainWindow : Window, new() - { - AfterSetup(builder => - { - var window = new TMainWindow(); - if (dataContextProvider != null) - window.DataContext = dataContextProvider(); - ((IClassicDesktopStyleApplicationLifetime)builder.Instance!.ApplicationLifetime!) - .MainWindow = window; - }); - - // Copy-pasted because we can't call extension methods due to generic constraints - var lifetime = new ClassicDesktopStyleApplicationLifetime() {ShutdownMode = ShutdownMode.OnMainWindowClose}; - SetupWithLifetime(lifetime); - lifetime.Start(Array.Empty()); - } - public delegate void AppMainDelegate(Application app, string[] args); - - [Obsolete("Use either lifetimes or AppMain overload. See see https://github.com/AvaloniaUI/Avalonia/wiki/Application-lifetimes for details", true)] - public void Start() - { - throw new NotSupportedException(); - } - + public void Start(AppMainDelegate main, string[] args) { Setup(); @@ -234,6 +203,9 @@ namespace Avalonia.Controls protected virtual bool CheckSetup => true; + /// + /// Searches and initiates modules included with attribute. + /// private void SetupAvaloniaModules() { var moduleInitializers = from assembly in AppDomain.CurrentDomain.GetAssemblies() diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs index 9860d0cb38..b38a539d4a 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls.ApplicationLifetimes { + [NotClientImplementable] public interface IApplicationLifetime { diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs index 2bd5c1238d..4b88f6b537 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Avalonia.Metadata; namespace Avalonia.Controls.ApplicationLifetimes { /// /// Controls application lifetime in classic desktop style /// + [NotClientImplementable] public interface IClassicDesktopStyleApplicationLifetime : IControlledApplicationLifetime { /// diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs index 3f61aeb536..d7eda790df 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Controls.ApplicationLifetimes { + [NotClientImplementable] public interface IControlledApplicationLifetime : IApplicationLifetime { /// diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs index e25815602e..480c65e5ad 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls.ApplicationLifetimes { + [NotClientImplementable] public interface ISingleViewApplicationLifetime : IApplicationLifetime { Control? MainView { get; set; } diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 4d239e69f4..c4587a2d9c 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -2,9 +2,6 @@ net6.0;netstandard2.0 - - - @@ -14,4 +11,10 @@ + + + + + + diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 0ef1ba4c8c..6dba33516b 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -232,6 +232,13 @@ namespace Avalonia.Controls StopListeningForDefault(inputElement); } } + if (IsCancel) + { + if (e.Root is IInputElement inputElement) + { + StopListeningForCancel(inputElement); + } + } } /// @@ -309,6 +316,8 @@ namespace Avalonia.Controls IsPressed = false; e.Handled = true; } + + base.OnKeyUp(e); } /// @@ -393,6 +402,8 @@ namespace Avalonia.Controls /// protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) { + base.OnPointerCaptureLost(e); + IsPressed = false; } @@ -407,6 +418,8 @@ namespace Avalonia.Controls /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { + base.OnApplyTemplate(e); + UnregisterFlyoutEvents(Flyout); RegisterFlyoutEvents(Flyout); UpdatePseudoClasses(); diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs index c44994f92f..4c958c83b7 100644 --- a/src/Avalonia.Controls/Calendar/CalendarItem.cs +++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using Avalonia.Collections.Pooled; using Avalonia.Controls.Metadata; using Avalonia.Data; using Avalonia.Input; @@ -172,13 +173,13 @@ namespace Avalonia.Controls.Primitives if (MonthView != null) { var childCount = Calendar.RowsPerMonth + Calendar.RowsPerMonth * Calendar.ColumnsPerMonth; - var children = new List(childCount); + using var children = new PooledList(childCount); for (int i = 0; i < Calendar.RowsPerMonth; i++) { if (_dayTitleTemplate != null) { - var cell = _dayTitleTemplate.Build(); + var cell = (Control) _dayTitleTemplate.Build(); cell.DataContext = string.Empty; cell.SetValue(Grid.RowProperty, 0); cell.SetValue(Grid.ColumnProperty, i); @@ -186,11 +187,16 @@ namespace Avalonia.Controls.Primitives } } + EventHandler cellMouseLeftButtonDown = Cell_MouseLeftButtonDown; + EventHandler cellMouseLeftButtonUp = Cell_MouseLeftButtonUp; + EventHandler cellMouseEntered = Cell_MouseEntered; + EventHandler cellClick = Cell_Click; + for (int i = 1; i < Calendar.RowsPerMonth; i++) { for (int j = 0; j < Calendar.ColumnsPerMonth; j++) { - CalendarDayButton cell = new CalendarDayButton(); + var cell = new CalendarDayButton(); if (Owner != null) { @@ -198,10 +204,10 @@ namespace Avalonia.Controls.Primitives } cell.SetValue(Grid.RowProperty, i); cell.SetValue(Grid.ColumnProperty, j); - cell.CalendarDayButtonMouseDown += Cell_MouseLeftButtonDown; - cell.CalendarDayButtonMouseUp += Cell_MouseLeftButtonUp; - cell.PointerEnter += Cell_MouseEnter; - cell.Click += Cell_Click; + cell.CalendarDayButtonMouseDown += cellMouseLeftButtonDown; + cell.CalendarDayButtonMouseUp += cellMouseLeftButtonUp; + cell.PointerEntered += cellMouseEntered; + cell.Click += cellClick; children.Add(cell); } } @@ -212,14 +218,17 @@ namespace Avalonia.Controls.Primitives if (YearView != null) { var childCount = Calendar.RowsPerYear * Calendar.ColumnsPerYear; - var children = new List(childCount); + using var children = new PooledList(childCount); + + EventHandler monthCalendarButtonMouseDown = Month_CalendarButtonMouseDown; + EventHandler monthCalendarButtonMouseUp = Month_CalendarButtonMouseUp; + EventHandler monthMouseEntered = Month_MouseEntered; - CalendarButton month; for (int i = 0; i < Calendar.RowsPerYear; i++) { for (int j = 0; j < Calendar.ColumnsPerYear; j++) { - month = new CalendarButton(); + var month = new CalendarButton(); if (Owner != null) { @@ -227,9 +236,9 @@ namespace Avalonia.Controls.Primitives } month.SetValue(Grid.RowProperty, i); month.SetValue(Grid.ColumnProperty, j); - month.CalendarLeftMouseButtonDown += Month_CalendarButtonMouseDown; - month.CalendarLeftMouseButtonUp += Month_CalendarButtonMouseUp; - month.PointerEnter += Month_MouseEnter; + month.CalendarLeftMouseButtonDown += monthCalendarButtonMouseDown; + month.CalendarLeftMouseButtonUp += monthCalendarButtonMouseUp; + month.PointerEntered += monthMouseEntered; children.Add(month); } } @@ -867,7 +876,7 @@ namespace Avalonia.Controls.Primitives } } - internal void Cell_MouseEnter(object? sender, PointerEventArgs e) + internal void Cell_MouseEntered(object? sender, PointerEventArgs e) { if (Owner != null) { @@ -1153,7 +1162,7 @@ namespace Avalonia.Controls.Primitives } } - private void Month_MouseEnter(object? sender, PointerEventArgs e) + private void Month_MouseEntered(object? sender, PointerEventArgs e) { if (_isMouseLeftButtonDownYearView) { diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs new file mode 100644 index 0000000000..6c2356b411 --- /dev/null +++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs @@ -0,0 +1,303 @@ +using System; +using Avalonia.Controls.Primitives; +using Avalonia.Data; +using Avalonia.Layout; + +namespace Avalonia.Controls +{ + /// + public partial class CalendarDatePicker + { + /// + /// Defines the property. + /// + public static readonly DirectProperty DisplayDateProperty = + AvaloniaProperty.RegisterDirect( + nameof(DisplayDate), + o => o.DisplayDate, + (o, v) => o.DisplayDate = v); + + /// + /// Defines the property. + /// + public static readonly DirectProperty DisplayDateStartProperty = + AvaloniaProperty.RegisterDirect( + nameof(DisplayDateStart), + o => o.DisplayDateStart, + (o, v) => o.DisplayDateStart = v); + + /// + /// Defines the property. + /// + public static readonly DirectProperty DisplayDateEndProperty = + AvaloniaProperty.RegisterDirect( + nameof(DisplayDateEnd), + o => o.DisplayDateEnd, + (o, v) => o.DisplayDateEnd = v); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FirstDayOfWeekProperty = + AvaloniaProperty.Register(nameof(FirstDayOfWeek)); + + /// + /// Defines the property. + /// + public static readonly DirectProperty IsDropDownOpenProperty = + AvaloniaProperty.RegisterDirect( + nameof(IsDropDownOpen), + o => o.IsDropDownOpen, + (o, v) => o.IsDropDownOpen = v); + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsTodayHighlightedProperty = + AvaloniaProperty.Register(nameof(IsTodayHighlighted)); + + /// + /// Defines the property. + /// + public static readonly DirectProperty SelectedDateProperty = + AvaloniaProperty.RegisterDirect( + nameof(SelectedDate), + o => o.SelectedDate, + (o, v) => o.SelectedDate = v, + enableDataValidation: true, + defaultBindingMode:BindingMode.TwoWay); + + /// + /// Defines the property. + /// + public static readonly StyledProperty SelectedDateFormatProperty = + AvaloniaProperty.Register( + nameof(SelectedDateFormat), + defaultValue: CalendarDatePickerFormat.Short, + validate: IsValidSelectedDateFormat); + + /// + /// Defines the property. + /// + public static readonly StyledProperty CustomDateFormatStringProperty = + AvaloniaProperty.Register( + nameof(CustomDateFormatString), + defaultValue: "d", + validate: IsValidDateFormatString); + + /// + /// Defines the property. + /// + public static readonly DirectProperty TextProperty = + AvaloniaProperty.RegisterDirect( + nameof(Text), + o => o.Text, + (o, v) => o.Text = v); + + /// + /// Defines the property. + /// + public static readonly StyledProperty WatermarkProperty = + TextBox.WatermarkProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty UseFloatingWatermarkProperty = + TextBox.UseFloatingWatermarkProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty HorizontalContentAlignmentProperty = + ContentControl.HorizontalContentAlignmentProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty VerticalContentAlignmentProperty = + ContentControl.VerticalContentAlignmentProperty.AddOwner(); + + /// + /// Gets a collection of dates that are marked as not selectable. + /// + /// + /// A collection of dates that cannot be selected. The default value is + /// an empty collection. + /// + public CalendarBlackoutDatesCollection? BlackoutDates { get; private set; } + + /// + /// Gets or sets the date to display. + /// + /// + /// The date to display. The default is . + /// + /// + /// The specified date is not in the range defined by + /// + /// and + /// . + /// + public DateTime DisplayDate + { + get => _displayDate; + set => SetAndRaise(DisplayDateProperty, ref _displayDate, value); + } + + /// + /// Gets or sets the first date to be displayed. + /// + /// The first date to display. + public DateTime? DisplayDateStart + { + get => _displayDateStart; + set => SetAndRaise(DisplayDateStartProperty, ref _displayDateStart, value); + } + + /// + /// Gets or sets the last date to be displayed. + /// + /// The last date to display. + public DateTime? DisplayDateEnd + { + get => _displayDateEnd; + set => SetAndRaise(DisplayDateEndProperty, ref _displayDateEnd, value); + } + + /// + /// Gets or sets the day that is considered the beginning of the week. + /// + /// + /// A representing the beginning of + /// the week. The default is . + /// + public DayOfWeek FirstDayOfWeek + { + get => GetValue(FirstDayOfWeekProperty); + set => SetValue(FirstDayOfWeekProperty, value); + } + + /// + /// Gets or sets a value indicating whether the drop-down + /// is open or closed. + /// + /// + /// True if the is + /// open; otherwise, false. The default is false. + /// + public bool IsDropDownOpen + { + get => _isDropDownOpen; + set => SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); + } + + /// + /// Gets or sets a value indicating whether the current date will be + /// highlighted. + /// + /// + /// True if the current date is highlighted; otherwise, false. The + /// default is true. + /// + public bool IsTodayHighlighted + { + get => GetValue(IsTodayHighlightedProperty); + set => SetValue(IsTodayHighlightedProperty, value); + } + + /// + /// Gets or sets the currently selected date. + /// + /// + /// The date currently selected. The default is null. + /// + /// + /// The specified date is not in the range defined by + /// + /// and + /// , + /// or the specified date is in the + /// + /// collection. + /// + public DateTime? SelectedDate + { + get => _selectedDate; + set => SetAndRaise(SelectedDateProperty, ref _selectedDate, value); + } + + /// + /// Gets or sets the format that is used to display the selected date. + /// + /// + /// The format that is used to display the selected date. The default is + /// . + /// + /// + /// An specified format is not valid. + /// + public CalendarDatePickerFormat SelectedDateFormat + { + get => GetValue(SelectedDateFormatProperty); + set => SetValue(SelectedDateFormatProperty, value); + } + + public string CustomDateFormatString + { + get => GetValue(CustomDateFormatStringProperty); + set => SetValue(CustomDateFormatStringProperty, value); + } + + /// + /// Gets or sets the text that is displayed by the . + /// + /// + /// The text displayed by the . + /// + /// + /// The text entered cannot be parsed to a valid date, and the exception + /// is not suppressed. + /// + /// + /// The text entered parses to a date that is not selectable. + /// + public string? Text + { + get => _text; + set => SetAndRaise(TextProperty, ref _text, value); + } + + /// + public string? Watermark + { + get => GetValue(WatermarkProperty); + set => SetValue(WatermarkProperty, value); + } + + /// + public bool UseFloatingWatermark + { + get => GetValue(UseFloatingWatermarkProperty); + set => SetValue(UseFloatingWatermarkProperty, value); + } + + /// + /// Gets or sets the horizontal alignment of the content within the control. + /// + public HorizontalAlignment HorizontalContentAlignment + { + get => GetValue(HorizontalContentAlignmentProperty); + set => SetValue(HorizontalContentAlignmentProperty, value); + } + + /// + /// Gets or sets the vertical alignment of the content within the control. + /// + public VerticalAlignment VerticalContentAlignment + { + get => GetValue(VerticalContentAlignmentProperty); + set => SetValue(VerticalContentAlignmentProperty, value); + } + } +} diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs similarity index 55% rename from src/Avalonia.Controls/Calendar/CalendarDatePicker.cs rename to src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs index 0409eb30aa..3d592e9ab5 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs @@ -7,122 +7,28 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; +using System.Reactive.Disposables; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.Layout; namespace Avalonia.Controls { /// - /// Provides data for the - /// - /// event. + /// A date selection control that allows the user to select dates from a drop down calendar. /// - public class CalendarDatePickerDateValidationErrorEventArgs : EventArgs - { - private bool _throwException; - - /// - /// Initializes a new instance of the - /// - /// class. - /// - /// - /// The initial exception from the - /// - /// event. - /// - /// - /// The text that caused the - /// - /// event. - /// - public CalendarDatePickerDateValidationErrorEventArgs(Exception exception, string text) - { - this.Text = text; - this.Exception = exception; - } - - /// - /// Gets the initial exception associated with the - /// - /// event. - /// - /// - /// The exception associated with the validation failure. - /// - public Exception Exception { get; private set; } - - /// - /// Gets the text that caused the - /// - /// event. - /// - /// - /// The text that caused the validation failure. - /// - public string Text { get; private set; } - - /// - /// Gets or sets a value indicating whether - /// - /// should be thrown. - /// - /// - /// True if the exception should be thrown; otherwise, false. - /// - /// - /// If set to true and - /// - /// is null. - /// - public bool ThrowException - { - get { return this._throwException; } - set - { - if (value && this.Exception == null) - { - throw new ArgumentException("Cannot Throw Null Exception"); - } - this._throwException = value; - } - } - } - - /// - /// Specifies date formats for a - /// . - /// - public enum CalendarDatePickerFormat - { - /// - /// Specifies that the date should be displayed using unabbreviated days - /// of the week and month names. - /// - Long = 0, - - /// - /// Specifies that the date should be displayed using abbreviated days - /// of the week and month names. - /// - Short = 1, - - /// - /// Specifies that the date should be displayed using a custom format string. - /// - Custom = 2 - } - [TemplatePart(ElementButton, typeof(Button))] [TemplatePart(ElementCalendar, typeof(Calendar))] [TemplatePart(ElementPopup, typeof(Popup))] [TemplatePart(ElementTextBox, typeof(TextBox))] - public class CalendarDatePicker : TemplatedControl + [PseudoClasses(pcFlyoutOpen, pcPressed)] + public partial class CalendarDatePicker : TemplatedControl { + protected const string pcPressed = ":pressed"; + protected const string pcFlyoutOpen = ":flyout-open"; + private const string ElementTextBox = "PART_TextBox"; private const string ElementButton = "PART_Button"; private const string ElementPopup = "PART_Popup"; @@ -131,8 +37,6 @@ namespace Avalonia.Controls private Calendar? _calendar; private string _defaultText; private Button? _dropDownButton; - //private Canvas _outsideCanvas; - //private Canvas _outsidePopupCanvas; private Popup? _popUp; private TextBox? _textBox; private IDisposable? _textBoxTextChangedSubscription; @@ -150,258 +54,8 @@ namespace Avalonia.Controls private bool _suspendTextChangeHandler = false; private bool _isPopupClosing = false; private bool _ignoreButtonClick = false; - - /// - /// Gets a collection of dates that are marked as not selectable. - /// - /// - /// A collection of dates that cannot be selected. The default value is - /// an empty collection. - /// - public CalendarBlackoutDatesCollection? BlackoutDates { get; private set; } - - public static readonly DirectProperty DisplayDateProperty = - AvaloniaProperty.RegisterDirect( - nameof(DisplayDate), - o => o.DisplayDate, - (o, v) => o.DisplayDate = v); - public static readonly DirectProperty DisplayDateStartProperty = - AvaloniaProperty.RegisterDirect( - nameof(DisplayDateStart), - o => o.DisplayDateStart, - (o, v) => o.DisplayDateStart = v); - public static readonly DirectProperty DisplayDateEndProperty = - AvaloniaProperty.RegisterDirect( - nameof(DisplayDateEnd), - o => o.DisplayDateEnd, - (o, v) => o.DisplayDateEnd = v); - public static readonly StyledProperty FirstDayOfWeekProperty = - AvaloniaProperty.Register(nameof(FirstDayOfWeek)); - - public static readonly DirectProperty IsDropDownOpenProperty = - AvaloniaProperty.RegisterDirect( - nameof(IsDropDownOpen), - o => o.IsDropDownOpen, - (o, v) => o.IsDropDownOpen = v); - - public static readonly StyledProperty IsTodayHighlightedProperty = - AvaloniaProperty.Register(nameof(IsTodayHighlighted)); - public static readonly DirectProperty SelectedDateProperty = - AvaloniaProperty.RegisterDirect( - nameof(SelectedDate), - o => o.SelectedDate, - (o, v) => o.SelectedDate = v, - enableDataValidation: true, - defaultBindingMode:BindingMode.TwoWay); - - public static readonly StyledProperty SelectedDateFormatProperty = - AvaloniaProperty.Register( - nameof(SelectedDateFormat), - defaultValue: CalendarDatePickerFormat.Short, - validate: IsValidSelectedDateFormat); - - public static readonly StyledProperty CustomDateFormatStringProperty = - AvaloniaProperty.Register( - nameof(CustomDateFormatString), - defaultValue: "d", - validate: IsValidDateFormatString); - - public static readonly DirectProperty TextProperty = - AvaloniaProperty.RegisterDirect( - nameof(Text), - o => o.Text, - (o, v) => o.Text = v); - public static readonly StyledProperty WatermarkProperty = - TextBox.WatermarkProperty.AddOwner(); - public static readonly StyledProperty UseFloatingWatermarkProperty = - TextBox.UseFloatingWatermarkProperty.AddOwner(); - - - /// - /// Defines the property. - /// - public static readonly StyledProperty HorizontalContentAlignmentProperty = - ContentControl.HorizontalContentAlignmentProperty.AddOwner(); - - /// - /// Defines the property. - /// - public static readonly StyledProperty VerticalContentAlignmentProperty = - ContentControl.VerticalContentAlignmentProperty.AddOwner(); - - /// - /// Gets or sets the date to display. - /// - /// - /// The date to display. The default - /// . - /// - /// - /// The specified date is not in the range defined by - /// - /// and - /// . - /// - public DateTime DisplayDate - { - get { return _displayDate; } - set { SetAndRaise(DisplayDateProperty, ref _displayDate, value); } - } - - /// - /// Gets or sets the first date to be displayed. - /// - /// The first date to display. - public DateTime? DisplayDateStart - { - get { return _displayDateStart; } - set { SetAndRaise(DisplayDateStartProperty, ref _displayDateStart, value); } - } - - /// - /// Gets or sets the last date to be displayed. - /// - /// The last date to display. - public DateTime? DisplayDateEnd - { - get { return _displayDateEnd; } - set { SetAndRaise(DisplayDateEndProperty, ref _displayDateEnd, value); } - } - - /// - /// Gets or sets the day that is considered the beginning of the week. - /// - /// - /// A representing the beginning of - /// the week. The default is . - /// - public DayOfWeek FirstDayOfWeek - { - get { return GetValue(FirstDayOfWeekProperty); } - set { SetValue(FirstDayOfWeekProperty, value); } - } - - /// - /// Gets or sets a value indicating whether the drop-down - /// is open or closed. - /// - /// - /// True if the is - /// open; otherwise, false. The default is false. - /// - public bool IsDropDownOpen - { - get { return _isDropDownOpen; } - set { SetAndRaise(IsDropDownOpenProperty, ref _isDropDownOpen, value); } - } - - /// - /// Gets or sets a value indicating whether the current date will be - /// highlighted. - /// - /// - /// True if the current date is highlighted; otherwise, false. The - /// default is true. - /// - public bool IsTodayHighlighted - { - get { return GetValue(IsTodayHighlightedProperty); } - set { SetValue(IsTodayHighlightedProperty, value); } - } - - /// - /// Gets or sets the currently selected date. - /// - /// - /// The date currently selected. The default is null. - /// - /// - /// The specified date is not in the range defined by - /// - /// and - /// , - /// or the specified date is in the - /// - /// collection. - /// - public DateTime? SelectedDate - { - get { return _selectedDate; } - set { SetAndRaise(SelectedDateProperty, ref _selectedDate, value); } - } - - /// - /// Gets or sets the format that is used to display the selected date. - /// - /// - /// The format that is used to display the selected date. The default is - /// . - /// - /// - /// An specified format is not valid. - /// - public CalendarDatePickerFormat SelectedDateFormat - { - get { return GetValue(SelectedDateFormatProperty); } - set { SetValue(SelectedDateFormatProperty, value); } - } - - public string CustomDateFormatString - { - get { return GetValue(CustomDateFormatStringProperty); } - set { SetValue(CustomDateFormatStringProperty, value); } - } - - /// - /// Gets or sets the text that is displayed by the - /// . - /// - /// - /// The text displayed by the - /// . - /// - /// - /// The text entered cannot be parsed to a valid date, and the exception - /// is not suppressed. - /// - /// - /// The text entered parses to a date that is not selectable. - /// - public string? Text - { - get { return _text; } - set { SetAndRaise(TextProperty, ref _text, value); } - } - - public string? Watermark - { - get { return GetValue(WatermarkProperty); } - set { SetValue(WatermarkProperty, value); } - } - public bool UseFloatingWatermark - { - get { return GetValue(UseFloatingWatermarkProperty); } - set { SetValue(UseFloatingWatermarkProperty, value); } - } - - - /// - /// Gets or sets the horizontal alignment of the content within the control. - /// - public HorizontalAlignment HorizontalContentAlignment - { - get => GetValue(HorizontalContentAlignmentProperty); - set => SetValue(HorizontalContentAlignmentProperty, value); - } - - /// - /// Gets or sets the vertical alignment of the content within the control. - /// - public VerticalAlignment VerticalContentAlignment - { - get => GetValue(VerticalContentAlignmentProperty); - set => SetValue(VerticalContentAlignmentProperty, value); - } + private bool _isFlyoutOpen = false; + private bool _isPressed = false; /// /// Occurs when the drop-down @@ -431,16 +85,10 @@ namespace Avalonia.Controls static CalendarDatePicker() { FocusableProperty.OverrideDefaultValue(true); - - IsDropDownOpenProperty.Changed.AddClassHandler((x,e) => x.OnIsDropDownOpenChanged(e)); - SelectedDateProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateChanged(e)); - SelectedDateFormatProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateFormatChanged(e)); - CustomDateFormatStringProperty.Changed.AddClassHandler((x,e) => x.OnCustomDateFormatStringChanged(e)); - TextProperty.Changed.AddClassHandler((x,e) => x.OnTextChanged(e)); } + /// - /// Initializes a new instance of the - /// class. + /// Initializes a new instance of the class. /// public CalendarDatePicker() { @@ -449,6 +97,16 @@ namespace Avalonia.Controls DisplayDate = DateTime.Today; } + /// + /// Updates the visual state of the control by applying latest PseudoClasses. + /// + protected void UpdatePseudoClasses() + { + PseudoClasses.Set(pcFlyoutOpen, _isFlyoutOpen); + PseudoClasses.Set(pcPressed, _isPressed); + } + + /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { if (_calendar != null) @@ -505,8 +163,9 @@ namespace Avalonia.Controls if(_dropDownButton != null) { _dropDownButton.Click += DropDownButton_Click; - _buttonPointerPressedSubscription = - _dropDownButton.AddDisposableHandler(PointerPressedEvent, DropDownButton_PointerPressed, handledEventsToo: true); + _buttonPointerPressedSubscription = new CompositeDisposable( + _dropDownButton.AddDisposableHandler(PointerPressedEvent, DropDownButton_PointerPressed, handledEventsToo: true), + _dropDownButton.AddDisposableHandler(PointerReleasedEvent, DropDownButton_PointerReleased, handledEventsToo: true)); } if (_textBox != null) @@ -538,16 +197,200 @@ namespace Avalonia.Controls SetSelectedDate(); } } + + UpdatePseudoClasses(); + } + + /// + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + // CustomDateFormatString + if (change.Property == CustomDateFormatStringProperty) + { + if (SelectedDateFormat == CalendarDatePickerFormat.Custom) + { + OnDateFormatChanged(); + } + } + // IsDropDownOpen + else if (change.Property == IsDropDownOpenProperty) + { + var (oldValue, newValue) = change.GetOldAndNewValue(); + + if (_popUp != null && _popUp.Child != null) + { + if (newValue != oldValue) + { + if (_calendar!.DisplayMode != CalendarMode.Month) + { + _calendar.DisplayMode = CalendarMode.Month; + } + + if (newValue) + { + OpenDropDown(); + } + else + { + _popUp.IsOpen = false; + _isFlyoutOpen = _popUp.IsOpen; + _isPressed = false; + + UpdatePseudoClasses(); + OnCalendarClosed(new RoutedEventArgs()); + } + } + } + } + // SelectedDate + else if (change.Property == SelectedDateProperty) + { + var (removedDate, addedDate) = change.GetOldAndNewValue(); + + if (SelectedDate != null) + { + DateTime day = SelectedDate.Value; + + // When the SelectedDateProperty change is done from + // OnTextPropertyChanged method, two-way binding breaks if + // BeginInvoke is not used: + Threading.Dispatcher.UIThread.InvokeAsync(() => + { + _settingSelectedDate = true; + Text = DateTimeToString(day); + _settingSelectedDate = false; + OnDateSelected(addedDate, removedDate); + }); + + // When DatePickerDisplayDateFlag is TRUE, the SelectedDate + // change is coming from the Calendar UI itself, so, we + // shouldn't change the DisplayDate since it will automatically + // be changed by the Calendar + if ((day.Month != DisplayDate.Month || day.Year != DisplayDate.Year) && (_calendar == null || !_calendar.CalendarDatePickerDisplayDateFlag)) + { + DisplayDate = day; + } + + if(_calendar != null) + { + _calendar.CalendarDatePickerDisplayDateFlag = false; + } + } + else + { + _settingSelectedDate = true; + SetWaterMarkText(); + _settingSelectedDate = false; + OnDateSelected(addedDate, removedDate); + } + } + // SelectedDateFormat + else if (change.Property == SelectedDateFormatProperty) + { + OnDateFormatChanged(); + } + // Text + else if (change.Property == TextProperty) + { + var (oldValue, newValue) = change.GetOldAndNewValue(); + + if (!_suspendTextChangeHandler) + { + if (newValue != null) + { + if (_textBox != null) + { + _textBox.Text = newValue; + } + else + { + _defaultText = newValue; + } + + if (!_settingSelectedDate) + { + SetSelectedDate(); + } + } + else + { + if (!_settingSelectedDate) + { + _settingSelectedDate = true; + SelectedDate = null; + _settingSelectedDate = false; + } + } + } + else + { + SetWaterMarkText(); + } + } + + base.OnPropertyChanged(change); } + /// protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception? error) { if (property == SelectedDateProperty) { DataValidationErrors.SetError(this, error); } + + base.UpdateDataValidation(property, state, error); + } + + /// + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + { + e.Handled = true; + + _ignoreButtonClick = _isPopupClosing; + + _isPressed = true; + UpdatePseudoClasses(); + } } + /// + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + base.OnPointerReleased(e); + + if (_isPressed && e.InitialPressMouseButton == MouseButton.Left) + { + e.Handled = true; + + if (!_ignoreButtonClick) + { + TogglePopUp(); + } + else + { + _ignoreButtonClick = false; + } + + _isPressed = false; + UpdatePseudoClasses(); + } + } + + /// + protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) + { + base.OnPointerCaptureLost(e); + + _isPressed = false; + UpdatePseudoClasses(); + } + + /// protected override void OnPointerWheelChanged(PointerWheelEventArgs e) { base.OnPointerWheelChanged(e); @@ -562,6 +405,8 @@ namespace Avalonia.Controls } } } + + /// protected override void OnGotFocus(GotFocusEventArgs e) { base.OnGotFocus(e); @@ -576,78 +421,57 @@ namespace Avalonia.Controls } } } + + /// protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); + _isPressed = false; + UpdatePseudoClasses(); + SetSelectedDate(); } - - private void OnIsDropDownOpenChanged(AvaloniaPropertyChangedEventArgs e) + + /// + protected override void OnKeyUp(KeyEventArgs e) { - var oldValue = (bool)e.OldValue!; - var value = (bool)e.NewValue!; + var key = e.Key; - if (_popUp != null && _popUp.Child != null) + if ((key == Key.Space || key == Key.Enter) && IsEffectivelyEnabled) // Key.GamepadA is not currently supported + { + // Since the TextBox is used for direct date entry, + // it isn't supported to open the popup/flyout using these keys. + // Other controls open the popup/flyout here. + } + else if (key == Key.Down && e.KeyModifiers.HasAllFlags(KeyModifiers.Alt) && IsEffectivelyEnabled) { - if (value != oldValue) + // It is only possible to open the popup using these keys. + // This is important as the down key is handled by calendar. + // If down also closed the popup, the date would move 1 week + // and then close the popup. This isn't user friendly at all. + // (calendar doesn't mark as handled either). + // The Escape key will still close the popup. + if (IsDropDownOpen == false) { - if (_calendar!.DisplayMode != CalendarMode.Month) - { - _calendar.DisplayMode = CalendarMode.Month; - } + e.Handled = true; - if (value) + if (!_ignoreButtonClick) { - OpenDropDown(); + TogglePopUp(); } else { - _popUp.IsOpen = false; - OnCalendarClosed(new RoutedEventArgs()); + _ignoreButtonClick = false; } - } - } - } - private void OnSelectedDateChanged(AvaloniaPropertyChangedEventArgs e) - { - var addedDate = (DateTime?)e.NewValue; - var removedDate = (DateTime?)e.OldValue; - if (SelectedDate != null) - { - DateTime day = SelectedDate.Value; - - // When the SelectedDateProperty change is done from - // OnTextPropertyChanged method, two-way binding breaks if - // BeginInvoke is not used: - Threading.Dispatcher.UIThread.InvokeAsync(() => - { - _settingSelectedDate = true; - Text = DateTimeToString(day); - _settingSelectedDate = false; - OnDateSelected(addedDate, removedDate); - }); - - // When DatePickerDisplayDateFlag is TRUE, the SelectedDate - // change is coming from the Calendar UI itself, so, we - // shouldn't change the DisplayDate since it will automatically - // be changed by the Calendar - if ((day.Month != DisplayDate.Month || day.Year != DisplayDate.Year) && (_calendar == null || !_calendar.CalendarDatePickerDisplayDateFlag)) - { - DisplayDate = day; + UpdatePseudoClasses(); } - if(_calendar != null) - _calendar.CalendarDatePickerDisplayDateFlag = false; - } - else - { - _settingSelectedDate = true; - SetWaterMarkText(); - _settingSelectedDate = false; - OnDateSelected(addedDate, removedDate); } + + base.OnKeyUp(e); } + private void OnDateFormatChanged() { if (_textBox != null) @@ -672,54 +496,6 @@ namespace Avalonia.Controls } } } - private void OnSelectedDateFormatChanged(AvaloniaPropertyChangedEventArgs e) - { - OnDateFormatChanged(); - } - private void OnCustomDateFormatStringChanged(AvaloniaPropertyChangedEventArgs e) - { - if(SelectedDateFormat == CalendarDatePickerFormat.Custom) - { - OnDateFormatChanged(); - } - } - private void OnTextChanged(AvaloniaPropertyChangedEventArgs e) - { - var oldValue = (string?)e.OldValue; - var value = (string?)e.NewValue; - - if (!_suspendTextChangeHandler) - { - if (value != null) - { - if (_textBox != null) - { - _textBox.Text = value; - } - else - { - _defaultText = value; - } - if (!_settingSelectedDate) - { - SetSelectedDate(); - } - } - else - { - if (!_settingSelectedDate) - { - _settingSelectedDate = true; - SelectedDate = null; - _settingSelectedDate = false; - } - } - } - else - { - SetWaterMarkText(); - } - } /// /// Raises the @@ -735,6 +511,7 @@ namespace Avalonia.Controls { DateValidationError?.Invoke(this, e); } + private void OnDateSelected(DateTime? addedDate, DateTime? removedDate) { EventHandler? handler = this.SelectedDateChanged; @@ -756,10 +533,12 @@ namespace Avalonia.Controls handler(this, new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, removedItems, addedItems)); } } + private void OnCalendarClosed(EventArgs e) { CalendarClosed?.Invoke(this, e); } + private void OnCalendarOpened(EventArgs e) { CalendarOpened?.Invoke(this, e); @@ -769,7 +548,8 @@ namespace Avalonia.Controls { Focus(); IsDropDownOpen = false; - } + } + private void Calendar_DisplayDateChanged(object? sender, CalendarDateChangedEventArgs e) { if (e.AddedDate != this.DisplayDate) @@ -777,6 +557,7 @@ namespace Avalonia.Controls SetValue(DisplayDateProperty, (DateTime) e.AddedDate!); } } + private void Calendar_SelectedDatesChanged(object? sender, SelectionChangedEventArgs e) { Debug.Assert(e.AddedItems.Count < 2, "There should be less than 2 AddedItems!"); @@ -802,6 +583,7 @@ namespace Avalonia.Controls } } } + private void Calendar_PointerReleased(object? sender, PointerReleasedEventArgs e) { @@ -810,6 +592,7 @@ namespace Avalonia.Controls e.Handled = true; } } + private void Calendar_KeyDown(object? sender, KeyEventArgs e) { Calendar? c = sender as Calendar ?? throw new ArgumentException("Sender must be Calendar.", nameof(sender)); @@ -825,10 +608,12 @@ namespace Avalonia.Controls } } } + private void TextBox_GotFocus(object? sender, RoutedEventArgs e) { IsDropDownOpen = false; } + private void TextBox_KeyDown(object? sender, KeyEventArgs e) { if (!e.Handled) @@ -836,6 +621,7 @@ namespace Avalonia.Controls e.Handled = ProcessDatePickerKey(e); } } + private void TextBox_TextChanged() { if (_textBox != null) @@ -845,21 +631,33 @@ namespace Avalonia.Controls _suspendTextChangeHandler = false; } } + private void DropDownButton_PointerPressed(object? sender, PointerPressedEventArgs e) { _ignoreButtonClick = _isPopupClosing; + + _isPressed = true; + UpdatePseudoClasses(); + } + + private void DropDownButton_PointerReleased(object? sender, PointerReleasedEventArgs e) + { + _isPressed = false; + UpdatePseudoClasses(); } + private void DropDownButton_Click(object? sender, RoutedEventArgs e) { if (!_ignoreButtonClick) { - HandlePopUp(); + TogglePopUp(); } else { _ignoreButtonClick = false; } } + private void PopUp_Closed(object? sender, EventArgs e) { IsDropDownOpen = false; @@ -871,7 +669,11 @@ namespace Avalonia.Controls } } - private void HandlePopUp() + /// + /// Toggles the property to open/close the calendar popup. + /// This will automatically adjust control focus as well. + /// + private void TogglePopUp() { if (IsDropDownOpen) { @@ -880,24 +682,28 @@ namespace Avalonia.Controls } else { - ProcessTextBox(); + SetSelectedDate(); + IsDropDownOpen = true; + _calendar!.Focus(); } } + private void OpenDropDown() { if (_calendar != null) { _calendar.Focus(); - OpenPopUp(); + + // Open the PopUp + _onOpenSelectedDate = SelectedDate; + _popUp!.IsOpen = true; + _isFlyoutOpen = _popUp!.IsOpen; + + UpdatePseudoClasses(); _calendar.ResetStates(); OnCalendarOpened(new RoutedEventArgs()); } } - private void OpenPopUp() - { - _onOpenSelectedDate = SelectedDate; - _popUp!.IsOpen = true; - } /// /// Input text is parsed in the correct format and changed into a @@ -944,8 +750,10 @@ namespace Avalonia.Controls throw textParseError.Exception; } } + return null; } + private string? DateTimeToString(DateTime d) { DateTimeFormatInfo dtfi = DateTimeHelper.GetCurrentDateFormat(); @@ -959,11 +767,12 @@ namespace Avalonia.Controls case CalendarDatePickerFormat.Custom: return string.Format(CultureInfo.CurrentCulture, d.ToString(CustomDateFormatString, dtfi)); } + return null; } + private bool ProcessDatePickerKey(KeyEventArgs e) { - switch (e.Key) { case Key.Enter: @@ -975,20 +784,16 @@ namespace Avalonia.Controls { if ((e.KeyModifiers & KeyModifiers.Control) == KeyModifiers.Control) { - HandlePopUp(); + TogglePopUp(); return true; } break; } } + return false; } - private void ProcessTextBox() - { - SetSelectedDate(); - IsDropDownOpen = true; - _calendar!.Focus(); - } + private void SetSelectedDate() { if (_textBox != null) @@ -1037,6 +842,7 @@ namespace Avalonia.Controls } } } + private DateTime? SetTextBoxValue(string s) { if (string.IsNullOrEmpty(s)) @@ -1070,6 +876,7 @@ namespace Avalonia.Controls } } } + private void SetWaterMarkText() { if (_textBox != null) @@ -1111,32 +918,10 @@ namespace Avalonia.Controls || value == CalendarDatePickerFormat.Short || value == CalendarDatePickerFormat.Custom; } + private static bool IsValidDateFormatString(string formatString) { return !string.IsNullOrWhiteSpace(formatString); } - private static DateTime DiscardDayTime(DateTime d) - { - int year = d.Year; - int month = d.Month; - DateTime newD = new DateTime(year, month, 1, 0, 0, 0); - return newD; - } - private static DateTime? DiscardTime(DateTime? d) - { - if (d == null) - { - return null; - } - else - { - DateTime discarded = (DateTime) d; - int year = discarded.Year; - int month = discarded.Month; - int day = discarded.Day; - DateTime newD = new DateTime(year, month, day, 0, 0, 0); - return newD; - } - } } } diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs new file mode 100644 index 0000000000..647910cb6b --- /dev/null +++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerDateValidationErrorEventArgs.cs @@ -0,0 +1,87 @@ +// (c) Copyright Microsoft Corporation. +// This source is subject to the Microsoft Public License (Ms-PL). +// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. +// All other rights reserved. + +using System; + +namespace Avalonia.Controls +{ + /// + /// Provides data for the + /// + /// event. + /// + public class CalendarDatePickerDateValidationErrorEventArgs : EventArgs + { + private bool _throwException; + + /// + /// Initializes a new instance of the + /// + /// class. + /// + /// + /// The initial exception from the + /// + /// event. + /// + /// + /// The text that caused the + /// + /// event. + /// + public CalendarDatePickerDateValidationErrorEventArgs(Exception exception, string text) + { + Text = text; + Exception = exception; + } + + /// + /// Gets the initial exception associated with the + /// + /// event. + /// + /// + /// The exception associated with the validation failure. + /// + public Exception Exception { get; private set; } + + /// + /// Gets the text that caused the + /// + /// event. + /// + /// + /// The text that caused the validation failure. + /// + public string Text { get; private set; } + + /// + /// Gets or sets a value indicating whether + /// + /// should be thrown. + /// + /// + /// True if the exception should be thrown; otherwise, false. + /// + /// + /// If set to true and + /// + /// is null. + /// + public bool ThrowException + { + get => _throwException; + set + { + if (value && Exception == null) + { + throw new ArgumentException("Cannot Throw Null Exception"); + } + + _throwException = value; + } + } + } +} diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs new file mode 100644 index 0000000000..4d96859d75 --- /dev/null +++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePickerFormat.cs @@ -0,0 +1,31 @@ +// (c) Copyright Microsoft Corporation. +// This source is subject to the Microsoft Public License (Ms-PL). +// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. +// All other rights reserved. + +namespace Avalonia.Controls +{ + /// + /// Specifies date formats for a + /// . + /// + public enum CalendarDatePickerFormat + { + /// + /// Specifies that the date should be displayed using unabbreviated days + /// of the week and month names. + /// + Long = 0, + + /// + /// Specifies that the date should be displayed using abbreviated days + /// of the week and month names. + /// + Short = 1, + + /// + /// Specifies that the date should be displayed using a custom format string. + /// + Custom = 2 + } +} diff --git a/src/Avalonia.Controls/Carousel.cs b/src/Avalonia.Controls/Carousel.cs index 4cacf51fa1..28a4aa6436 100644 --- a/src/Avalonia.Controls/Carousel.cs +++ b/src/Avalonia.Controls/Carousel.cs @@ -20,8 +20,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly StyledProperty PageTransitionProperty = - AvaloniaProperty.Register(nameof(PageTransition)); + public static readonly StyledProperty PageTransitionProperty = + AvaloniaProperty.Register(nameof(PageTransition)); /// /// The default value of for @@ -54,7 +54,7 @@ namespace Avalonia.Controls /// /// Gets or sets the transition to use when moving between pages. /// - public IPageTransition PageTransition + public IPageTransition? PageTransition { get { return GetValue(PageTransitionProperty); } set { SetValue(PageTransitionProperty, value); } diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index cbf9b35a05..05be5ad00d 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -181,26 +181,13 @@ namespace Avalonia.Controls protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - this.UpdateSelectionBoxItem(SelectedItem); + UpdateSelectionBoxItem(SelectedItem); } - // Because the SelectedItem isn't connected to the visual tree public override void InvalidateMirrorTransform() { base.InvalidateMirrorTransform(); - - if (SelectedItem is Control selectedControl) - { - selectedControl.InvalidateMirrorTransform(); - - foreach (var visual in selectedControl.GetVisualDescendants()) - { - if (visual is Control childControl) - { - childControl.InvalidateMirrorTransform(); - } - } - } + UpdateFlowDirection(); } /// @@ -365,6 +352,8 @@ namespace Avalonia.Controls { parent.GetObservable(IsVisibleProperty).Subscribe(IsVisibleChanged).DisposeWith(_subscriptionsOnOpen); } + + UpdateFlowDirection(); } private void IsVisibleChanged(bool isVisible) @@ -432,6 +421,8 @@ namespace Avalonia.Controls } }; } + + UpdateFlowDirection(); } else { @@ -439,6 +430,19 @@ namespace Avalonia.Controls } } + private void UpdateFlowDirection() + { + if (SelectionBoxItem is Rectangle rectangle) + { + if ((rectangle.Fill as VisualBrush)?.Visual is Control content) + { + var flowDirection = (((IVisual)content!).VisualParent as Control)?.FlowDirection ?? + FlowDirection.LeftToRight; + rectangle.FlowDirection = flowDirection; + } + } + } + private void SelectFocusedItem() { foreach (ItemContainerInfo dropdownItem in ItemContainerGenerator.Containers) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 2b122d4174..7b35e35278 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -450,6 +450,11 @@ namespace Avalonia.Controls if (sender is Control control && control.ContextMenu is ContextMenu contextMenu) { + if (contextMenu._popup?.Parent == control) + { + ((ISetLogicalParent)contextMenu._popup).SetParent(null); + } + contextMenu.Close(); } } diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index d6a5fa0727..083182a370 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using Avalonia.Automation.Peers; using Avalonia.Controls.Documents; @@ -10,6 +11,7 @@ using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Rendering; using Avalonia.Styling; +using Avalonia.Threading; using Avalonia.VisualTree; namespace Avalonia.Controls @@ -53,21 +55,57 @@ namespace Avalonia.Controls /// Event raised when an element wishes to be scrolled into view. /// public static readonly RoutedEvent RequestBringIntoViewEvent = - RoutedEvent.Register("RequestBringIntoView", RoutingStrategies.Bubble); + RoutedEvent.Register( + "RequestBringIntoView", + RoutingStrategies.Bubble); /// /// Provides event data for the event. /// public static readonly RoutedEvent ContextRequestedEvent = - RoutedEvent.Register(nameof(ContextRequested), + RoutedEvent.Register( + nameof(ContextRequested), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); + /// + /// Defines the event. + /// + public static readonly RoutedEvent LoadedEvent = + RoutedEvent.Register( + nameof(Loaded), + RoutingStrategies.Direct); + + /// + /// Defines the event. + /// + public static readonly RoutedEvent UnloadedEvent = + RoutedEvent.Register( + nameof(Unloaded), + RoutingStrategies.Direct); + /// /// Defines the property. /// public static readonly AttachedProperty FlowDirectionProperty = - AvaloniaProperty.RegisterAttached(nameof(FlowDirection), inherits: true); - + AvaloniaProperty.RegisterAttached( + nameof(FlowDirection), + inherits: true); + + // Note the following: + // _loadedQueue : + // Is the queue where any control will be added to indicate that its loaded + // event should be scheduled and called later. + // _loadedProcessingQueue : + // Contains a copied snapshot of the _loadedQueue at the time when processing + // starts and individual events are being fired. This was needed to avoid + // exceptions if new controls were added in the Loaded event itself. + + private static bool _isLoadedProcessing = false; + private static readonly HashSet _loadedQueue = new HashSet(); + private static readonly HashSet _loadedProcessingQueue = new HashSet(); + + private bool _isAttachedToVisualTree = false; + private bool _isLoaded = false; private DataTemplates? _dataTemplates; private IControl? _focusAdorner; private AutomationPeer? _automationPeer; @@ -108,6 +146,15 @@ namespace Avalonia.Controls set => SetValue(ContextFlyoutProperty, value); } + /// + /// Gets a value indicating whether the control is fully constructed in the visual tree + /// and both layout and render are complete. + /// + /// + /// This is set to true while raising the event. + /// + public bool IsLoaded => _isLoaded; + /// /// Gets or sets a user-defined object attached to the control. /// @@ -135,6 +182,35 @@ namespace Avalonia.Controls remove => RemoveHandler(ContextRequestedEvent, value); } + /// + /// Occurs when the control has been fully constructed in the visual tree and both + /// layout and render are complete. + /// + /// + /// This event is guaranteed to occur after the control template is applied and references + /// to objects created after the template is applied are available. This makes it different + /// from OnAttachedToVisualTree which doesn't have these references. This event occurs at the + /// latest possible time in the control creation life-cycle. + /// + public event EventHandler? Loaded + { + add => AddHandler(LoadedEvent, value); + remove => RemoveHandler(LoadedEvent, value); + } + + /// + /// Occurs when the control is removed from the visual tree. + /// + /// + /// This is API symmetrical with and exists for compatibility with other + /// XAML frameworks; however, it behaves the same as OnDetachedFromVisualTree. + /// + public event EventHandler? Unloaded + { + add => AddHandler(UnloadedEvent, value); + remove => RemoveHandler(UnloadedEvent, value); + } + public new IControl? Parent => (IControl?)base.Parent; /// @@ -215,18 +291,124 @@ namespace Avalonia.Controls /// The control that receives the focus adorner. protected virtual IControl? GetTemplateFocusTarget() => this; + private static Action loadedProcessingAction = () => + { + // Copy the loaded queue for processing + // There was a possibility of the "Collection was modified; enumeration operation may not execute." + // exception when only a single hash set was used. This could happen when new controls are added + // within the Loaded callback/event itself. To fix this, two hash sets are used and while one is + // being processed the other accepts adding new controls to process next. + _loadedProcessingQueue.Clear(); + foreach (Control control in _loadedQueue) + { + _loadedProcessingQueue.Add(control); + } + _loadedQueue.Clear(); + + foreach (Control control in _loadedProcessingQueue) + { + control.OnLoadedCore(); + } + + _loadedProcessingQueue.Clear(); + _isLoadedProcessing = false; + + // Restart if any controls were added to the queue while processing + if (_loadedQueue.Count > 0) + { + _isLoadedProcessing = true; + Dispatcher.UIThread.Post(loadedProcessingAction!, DispatcherPriority.Loaded); + } + }; + + /// + /// Schedules to be called for this control. + /// For performance, it will be queued with other controls. + /// + internal void ScheduleOnLoadedCore() + { + if (_isLoaded == false) + { + bool isAdded = _loadedQueue.Add(this); + + if (isAdded && + _isLoadedProcessing == false) + { + _isLoadedProcessing = true; + Dispatcher.UIThread.Post(loadedProcessingAction!, DispatcherPriority.Loaded); + } + } + } + + /// + /// Invoked as the first step of marking the control as loaded and raising the + /// event. + /// + internal void OnLoadedCore() + { + if (_isLoaded == false && + _isAttachedToVisualTree) + { + _isLoaded = true; + OnLoaded(); + } + } + + /// + /// Invoked as the first step of marking the control as unloaded and raising the + /// event. + /// + internal void OnUnloadedCore() + { + if (_isLoaded) + { + // Remove from the loaded event queue here as a failsafe in case the control + // is detached before the dispatcher runs the Loaded jobs. + _loadedQueue.Remove(this); + + _isLoaded = false; + OnUnloaded(); + } + } + + /// + /// Invoked just before the event. + /// + protected virtual void OnLoaded() + { + var eventArgs = new RoutedEventArgs(LoadedEvent); + eventArgs.Source = null; + RaiseEvent(eventArgs); + } + + /// + /// Invoked just before the event. + /// + protected virtual void OnUnloaded() + { + var eventArgs = new RoutedEventArgs(UnloadedEvent); + eventArgs.Source = null; + RaiseEvent(eventArgs); + } + /// protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTreeCore(e); + _isAttachedToVisualTree = true; InitializeIfNeeded(); + + ScheduleOnLoadedCore(); } /// protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTreeCore(e); + _isAttachedToVisualTree = false; + + OnUnloadedCore(); } /// @@ -324,7 +506,9 @@ namespace Avalonia.Controls var keymap = AvaloniaLocator.Current.GetService()?.OpenContextMenu; if (keymap is null) + { return; + } var matches = false; @@ -378,17 +562,12 @@ namespace Avalonia.Controls bool bypassFlowDirectionPolicies = BypassFlowDirectionPolicies; bool parentBypassFlowDirectionPolicies = false; - var parent = this.FindAncestorOfType(); + var parent = ((IVisual)this).VisualParent as Control; if (parent != null) { parentFlowDirection = parent.FlowDirection; parentBypassFlowDirectionPolicies = parent.BypassFlowDirectionPolicies; } - else if (Parent is Control logicalParent) - { - parentFlowDirection = logicalParent.FlowDirection; - parentBypassFlowDirectionPolicies = logicalParent.BypassFlowDirectionPolicies; - } bool thisShouldBeMirrored = flowDirection == FlowDirection.RightToLeft && !bypassFlowDirectionPolicies; bool parentShouldBeMirrored = parentFlowDirection == FlowDirection.RightToLeft && !parentBypassFlowDirectionPolicies; diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs index f04c79505e..047667567d 100644 --- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs @@ -38,13 +38,13 @@ namespace Avalonia.Controls /// Defines the property /// public static readonly StyledProperty HeaderProperty = - AvaloniaProperty.Register(nameof(Header)); + AvaloniaProperty.Register(nameof(Header)); /// /// Defines the property /// public static readonly StyledProperty HeaderTemplateProperty = - AvaloniaProperty.Register(nameof(HeaderTemplate)); + AvaloniaProperty.Register(nameof(HeaderTemplate)); /// /// Defines the property diff --git a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs index 7f2abb7e98..46c5eaeaaa 100644 --- a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs +++ b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs @@ -194,7 +194,7 @@ namespace Avalonia.Controls if (ClockIdentifier == "12HourClock") { - hr = per == 1 ? hr + 12 : per == 0 && hr == 12 ? 0 : hr; + hr = per == 1 ? (hr == 12) ? 12 : hr + 12 : per == 0 && hr == 12 ? 0 : hr; } Time = new TimeSpan(hr, min, 0); diff --git a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs index 45cd1d727e..64978248e5 100644 --- a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs +++ b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs @@ -1,11 +1,13 @@ using System; using Avalonia.Controls.Primitives; +using Avalonia.Metadata; namespace Avalonia.Controls.Diagnostics { /// /// Diagnostics interface to retrieve an associated . /// + [NotClientImplementable] public interface IPopupHostProvider { /// diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index 08d559a5c1..1c7bdb9b37 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using Avalonia.Input; using Avalonia.Input.Raw; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering; namespace Avalonia.Controls.Embedding.Offscreen { + [Unstable] public abstract class OffscreenTopLevelImplBase : ITopLevelImpl { private double _scaling = 1; diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs index d024f86b32..4801fa69f0 100644 --- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs +++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs @@ -175,7 +175,8 @@ namespace Avalonia.Controls.Primitives IsOpen = false; Popup.IsOpen = false; - + ((ISetLogicalParent)Popup).SetParent(null); + // Ensure this isn't active _transientDisposable?.Dispose(); _transientDisposable = null; @@ -218,7 +219,7 @@ namespace Avalonia.Controls.Primitives ((ISetLogicalParent)Popup).SetParent(null); } - if (Popup.PlacementTarget != placementTarget) + if (Popup.Parent == null || Popup.PlacementTarget != placementTarget) { Popup.PlacementTarget = Target = placementTarget; ((ISetLogicalParent)Popup).SetParent(placementTarget); diff --git a/src/Avalonia.Controls/GridSplitter.cs b/src/Avalonia.Controls/GridSplitter.cs index 216e43e1f0..85dad894fd 100644 --- a/src/Avalonia.Controls/GridSplitter.cs +++ b/src/Avalonia.Controls/GridSplitter.cs @@ -221,7 +221,8 @@ namespace Avalonia.Controls ShowsPreview = showsPreview, ResizeDirection = resizeDirection, SplitterLength = Math.Min(Bounds.Width, Bounds.Height), - ResizeBehavior = GetEffectiveResizeBehavior(resizeDirection) + ResizeBehavior = GetEffectiveResizeBehavior(resizeDirection), + Scaling = (VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1, }; // Store the rows and columns to resize on drag events. @@ -348,9 +349,9 @@ namespace Avalonia.Controls } } - protected override void OnPointerEnter(PointerEventArgs e) + protected override void OnPointerEntered(PointerEventArgs e) { - base.OnPointerEnter(e); + base.OnPointerEntered(e); GridResizeDirection direction = GetEffectiveResizeDirection(); @@ -630,13 +631,17 @@ namespace Avalonia.Controls { double actualLength1 = GetActualLength(definition1); double actualLength2 = GetActualLength(definition2); + double pixelLength = 1 / _resizeData.Scaling; + double epsilon = pixelLength + LayoutHelper.LayoutEpsilon; // When splitting, Check to see if the total pixels spanned by the definitions - // is the same as before starting resize. If not cancel the drag. + // is the same as before starting resize. If not cancel the drag. We need to account for + // layout rounding here, so ignore differences of less than a device pixel to avoid problems + // that WPF has, such as https://stackoverflow.com/questions/28464843. if (_resizeData.SplitBehavior == SplitBehavior.Split && !MathUtilities.AreClose( actualLength1 + actualLength2, - _resizeData.OriginalDefinition1ActualLength + _resizeData.OriginalDefinition2ActualLength, LayoutHelper.LayoutEpsilon)) + _resizeData.OriginalDefinition1ActualLength + _resizeData.OriginalDefinition2ActualLength, epsilon)) { CancelResize(); @@ -798,6 +803,9 @@ namespace Avalonia.Controls // The minimum of Width/Height of Splitter. Used to ensure splitter // isn't hidden by resizing a row/column smaller than the splitter. public double SplitterLength; + + // The current layout scaling factor. + public double Scaling; } } diff --git a/src/Avalonia.Controls/IContentControl.cs b/src/Avalonia.Controls/IContentControl.cs index d28b0afb25..b4d8d0f574 100644 --- a/src/Avalonia.Controls/IContentControl.cs +++ b/src/Avalonia.Controls/IContentControl.cs @@ -1,5 +1,6 @@ using Avalonia.Controls.Templates; using Avalonia.Layout; +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -7,6 +8,7 @@ namespace Avalonia.Controls /// Defines a control that displays according to a /// . /// + [NotClientImplementable] public interface IContentControl : IControl { /// diff --git a/src/Avalonia.Controls/IControl.cs b/src/Avalonia.Controls/IControl.cs index b501bc15a7..3395fc1059 100644 --- a/src/Avalonia.Controls/IControl.cs +++ b/src/Avalonia.Controls/IControl.cs @@ -1,6 +1,7 @@ using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Layout; +using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Controls @@ -8,6 +9,7 @@ namespace Avalonia.Controls /// /// Interface for Avalonia controls. /// + [NotClientImplementable] public interface IControl : IVisual, IDataTemplateHost, ILayoutable, diff --git a/src/Avalonia.Controls/IGlobalDataTemplates.cs b/src/Avalonia.Controls/IGlobalDataTemplates.cs index 92dcd2c189..f4499ddb5e 100644 --- a/src/Avalonia.Controls/IGlobalDataTemplates.cs +++ b/src/Avalonia.Controls/IGlobalDataTemplates.cs @@ -1,10 +1,12 @@ using Avalonia.Controls.Templates; +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Defines the application-global data templates. /// + [NotClientImplementable] public interface IGlobalDataTemplates : IDataTemplateHost { } diff --git a/src/Avalonia.Controls/IMenu.cs b/src/Avalonia.Controls/IMenu.cs index 0722a22f08..d90c5ea7a8 100644 --- a/src/Avalonia.Controls/IMenu.cs +++ b/src/Avalonia.Controls/IMenu.cs @@ -1,10 +1,12 @@ using Avalonia.Controls.Platform; +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Represents a or . /// + [NotClientImplementable] public interface IMenu : IMenuElement { /// diff --git a/src/Avalonia.Controls/IMenuElement.cs b/src/Avalonia.Controls/IMenuElement.cs index a3200d2b1b..c13c20b639 100644 --- a/src/Avalonia.Controls/IMenuElement.cs +++ b/src/Avalonia.Controls/IMenuElement.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using Avalonia.Input; +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Represents an or . /// + [NotClientImplementable] public interface IMenuElement : IControl { /// diff --git a/src/Avalonia.Controls/IMenuItem.cs b/src/Avalonia.Controls/IMenuItem.cs index 35e36eb0f4..9d7ef3c18d 100644 --- a/src/Avalonia.Controls/IMenuItem.cs +++ b/src/Avalonia.Controls/IMenuItem.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Controls +using Avalonia.Metadata; + +namespace Avalonia.Controls { /// /// Represents a . /// + [NotClientImplementable] public interface IMenuItem : IMenuElement { /// diff --git a/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs b/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs index f492e6ca0f..29963e4821 100644 --- a/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs +++ b/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls { + [Unstable] public interface INativeMenuExporterEventsImplBridge { void RaiseNeedsUpdate (); diff --git a/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs b/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs index 6cb68d8ddd..a6c7489971 100644 --- a/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs +++ b/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls { + [Unstable] public interface INativeMenuItemExporterEventsImplBridge { void RaiseClicked (); diff --git a/src/Avalonia.Controls/IPanel.cs b/src/Avalonia.Controls/IPanel.cs index 7b9e2c2074..8f2564ec74 100644 --- a/src/Avalonia.Controls/IPanel.cs +++ b/src/Avalonia.Controls/IPanel.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls { /// /// Interface for controls that can contain multiple children. /// + [NotClientImplementable] public interface IPanel : IControl { /// @@ -10,4 +13,4 @@ namespace Avalonia.Controls /// Controls Children { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/IScrollable.cs b/src/Avalonia.Controls/IScrollable.cs index 2a98b3910a..680088290c 100644 --- a/src/Avalonia.Controls/IScrollable.cs +++ b/src/Avalonia.Controls/IScrollable.cs @@ -1,4 +1,3 @@ - namespace Avalonia.Controls.Primitives { /// diff --git a/src/Avalonia.Controls/MenuBase.cs b/src/Avalonia.Controls/MenuBase.cs index bdcff5cd09..122d45d033 100644 --- a/src/Avalonia.Controls/MenuBase.cs +++ b/src/Avalonia.Controls/MenuBase.cs @@ -18,8 +18,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty IsOpenProperty = - AvaloniaProperty.RegisterDirect( + public static readonly DirectProperty IsOpenProperty = + AvaloniaProperty.RegisterDirect( nameof(IsOpen), o => o.IsOpen); diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 619eafb71b..11c42f2ef3 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -79,25 +79,33 @@ namespace Avalonia.Controls /// Defines the event. /// public static readonly RoutedEvent ClickEvent = - RoutedEvent.Register(nameof(Click), RoutingStrategies.Bubble); + RoutedEvent.Register( + nameof(Click), + RoutingStrategies.Bubble); /// - /// Defines the event. + /// Defines the event. /// - public static readonly RoutedEvent PointerEnterItemEvent = - RoutedEvent.Register(nameof(PointerEnterItem), RoutingStrategies.Bubble); + public static readonly RoutedEvent PointerEnteredItemEvent = + RoutedEvent.Register( + nameof(PointerEnteredItem), + RoutingStrategies.Bubble); /// - /// Defines the event. + /// Defines the event. /// - public static readonly RoutedEvent PointerLeaveItemEvent = - RoutedEvent.Register(nameof(PointerLeaveItem), RoutingStrategies.Bubble); + public static readonly RoutedEvent PointerExitedItemEvent = + RoutedEvent.Register( + nameof(PointerExitedItem), + RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent SubmenuOpenedEvent = - RoutedEvent.Register(nameof(SubmenuOpened), RoutingStrategies.Bubble); + RoutedEvent.Register( + nameof(SubmenuOpened), + RoutingStrategies.Bubble); /// /// The default value for the property. @@ -174,24 +182,24 @@ namespace Avalonia.Controls /// Occurs when the pointer enters a menu item. /// /// - /// A bubbling version of the event for menu items. + /// A bubbling version of the event for menu items. /// - public event EventHandler? PointerEnterItem + public event EventHandler? PointerEnteredItem { - add { AddHandler(PointerEnterItemEvent, value); } - remove { RemoveHandler(PointerEnterItemEvent, value); } + add { AddHandler(PointerEnteredItemEvent, value); } + remove { RemoveHandler(PointerEnteredItemEvent, value); } } /// /// Raised when the pointer leaves a menu item. /// /// - /// A bubbling version of the event for menu items. + /// A bubbling version of the event for menu items. /// - public event EventHandler? PointerLeaveItem + public event EventHandler? PointerExitedItem { - add { AddHandler(PointerLeaveItemEvent, value); } - remove { RemoveHandler(PointerLeaveItemEvent, value); } + add { AddHandler(PointerExitedItemEvent, value); } + remove { RemoveHandler(PointerExitedItemEvent, value); } } /// @@ -437,22 +445,22 @@ namespace Avalonia.Controls } /// - protected override void OnPointerEnter(PointerEventArgs e) + protected override void OnPointerEntered(PointerEventArgs e) { - base.OnPointerEnter(e); + base.OnPointerEntered(e); var point = e.GetCurrentPoint(null); - RaiseEvent(new PointerEventArgs(PointerEnterItemEvent, this, e.Pointer, this.VisualRoot, point.Position, + RaiseEvent(new PointerEventArgs(PointerEnteredItemEvent, this, e.Pointer, this.VisualRoot, point.Position, e.Timestamp, point.Properties, e.KeyModifiers)); } /// - protected override void OnPointerLeave(PointerEventArgs e) + protected override void OnPointerExited(PointerEventArgs e) { - base.OnPointerLeave(e); + base.OnPointerExited(e); var point = e.GetCurrentPoint(null); - RaiseEvent(new PointerEventArgs(PointerLeaveItemEvent, this, e.Pointer, this.VisualRoot, point.Position, + RaiseEvent(new PointerEventArgs(PointerExitedItemEvent, this, e.Pointer, this.VisualRoot, point.Position, e.Timestamp, point.Properties, e.KeyModifiers)); } diff --git a/src/Avalonia.Controls/NativeMenuItemBase.cs b/src/Avalonia.Controls/NativeMenuItemBase.cs index a5e3308e3b..4946d16f01 100644 --- a/src/Avalonia.Controls/NativeMenuItemBase.cs +++ b/src/Avalonia.Controls/NativeMenuItemBase.cs @@ -11,8 +11,8 @@ namespace Avalonia.Controls } - public static readonly DirectProperty ParentProperty = - AvaloniaProperty.RegisterDirect("Parent", o => o.Parent, (o, v) => o.Parent = v); + public static readonly DirectProperty ParentProperty = + AvaloniaProperty.RegisterDirect("Parent", o => o.Parent, (o, v) => o.Parent = v); public NativeMenu? Parent { diff --git a/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs b/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs index 977544674d..b2e6e9e80b 100644 --- a/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs +++ b/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Controls.Notifications +using Avalonia.Metadata; + +namespace Avalonia.Controls.Notifications { /// /// Represents a notification manager that can show arbitrary content. @@ -9,6 +11,7 @@ /// can display arbitrary content, as opposed to notification managers which display notifications /// using the host operating system's notification mechanism. /// + [NotClientImplementable] public interface IManagedNotificationManager : INotificationManager { /// diff --git a/src/Avalonia.Controls/Notifications/INotification.cs b/src/Avalonia.Controls/Notifications/INotification.cs index fa08233097..9ccce5b2c4 100644 --- a/src/Avalonia.Controls/Notifications/INotification.cs +++ b/src/Avalonia.Controls/Notifications/INotification.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Controls.Notifications { /// /// Represents a notification that can be shown in a window or by the host operating system. /// + [NotClientImplementable] public interface INotification { /// diff --git a/src/Avalonia.Controls/Notifications/INotificationManager.cs b/src/Avalonia.Controls/Notifications/INotificationManager.cs index 72fb8e6c08..5fa479f2c3 100644 --- a/src/Avalonia.Controls/Notifications/INotificationManager.cs +++ b/src/Avalonia.Controls/Notifications/INotificationManager.cs @@ -1,9 +1,12 @@ -namespace Avalonia.Controls.Notifications +using Avalonia.Metadata; + +namespace Avalonia.Controls.Notifications { /// /// Represents a notification manager that can be used to show notifications in a window or using /// the host operating system. /// + [NotClientImplementable] public interface INotificationManager { /// diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index 4d86a0f17c..705e68e3ea 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -107,8 +107,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty ValueProperty = - AvaloniaProperty.RegisterDirect(nameof(Value), updown => updown.Value, + public static readonly DirectProperty ValueProperty = + AvaloniaProperty.RegisterDirect(nameof(Value), updown => updown.Value, (updown, v) => updown.Value = v, defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true); /// @@ -131,7 +131,7 @@ namespace Avalonia.Controls private IDisposable? _textBoxTextChangedSubscription; - private decimal _value; + private decimal? _value; private string? _text; private bool _internalValueSet; private bool _clipValueToMinMax; @@ -277,7 +277,7 @@ namespace Avalonia.Controls /// /// Gets or sets the value. /// - public decimal Value + public decimal? Value { get { return _value; } set @@ -351,7 +351,7 @@ namespace Avalonia.Controls /// protected override void OnLostFocus(RoutedEventArgs e) { - CommitInput(); + CommitInput(true); base.OnLostFocus(e); } @@ -489,9 +489,9 @@ namespace Avalonia.Controls { SetValidSpinDirection(); } - if (ClipValueToMinMax) + if (ClipValueToMinMax && Value.HasValue) { - Value = MathUtilities.Clamp(Value, Minimum, Maximum); + Value = MathUtilities.Clamp(Value.Value, Minimum, Maximum); } } @@ -506,9 +506,9 @@ namespace Avalonia.Controls { SetValidSpinDirection(); } - if (ClipValueToMinMax) + if (ClipValueToMinMax && Value.HasValue) { - Value = MathUtilities.Clamp(Value, Minimum, Maximum); + Value = MathUtilities.Clamp(Value.Value, Minimum, Maximum); } } @@ -530,7 +530,7 @@ namespace Avalonia.Controls /// /// The old value. /// The new value. - protected virtual void OnValueChanged(decimal oldValue, decimal newValue) + protected virtual void OnValueChanged(decimal? oldValue, decimal? newValue) { if (!_internalValueSet && IsInitialized) { @@ -573,7 +573,7 @@ namespace Avalonia.Controls /// Called when the property has to be coerced. /// /// The value. - protected virtual decimal OnCoerceValue(decimal baseValue) + protected virtual decimal? OnCoerceValue(decimal? baseValue) { return baseValue; } @@ -607,7 +607,7 @@ namespace Avalonia.Controls /// /// The old value. /// The new value. - protected virtual void RaiseValueChangedEvent(decimal oldValue, decimal newValue) + protected virtual void RaiseValueChangedEvent(decimal? oldValue, decimal? newValue) { var e = new NumericUpDownValueChangedEventArgs(ValueChangedEvent, oldValue, newValue); RaiseEvent(e); @@ -616,9 +616,9 @@ namespace Avalonia.Controls /// /// Converts the formatted text to a value. /// - private decimal ConvertTextToValue(string text) + private decimal? ConvertTextToValue(string? text) { - decimal result = 0; + decimal? result = null; if (string.IsNullOrEmpty(text)) { @@ -635,9 +635,9 @@ namespace Avalonia.Controls result = ConvertTextToValueCore(currentValueText, text); - if (ClipValueToMinMax) + if (ClipValueToMinMax && result.HasValue) { - return MathUtilities.Clamp(result, Minimum, Maximum); + return MathUtilities.Clamp(result.Value, Minimum, Maximum); } ValidateMinMax(result); @@ -649,7 +649,7 @@ namespace Avalonia.Controls /// Converts the value to formatted text. /// /// - private string ConvertValueToText() + private string? ConvertValueToText() { //Manage FormatString of type "{}{0:N2} °" (in xaml) or "{0:N2} °" in code-behind. if (FormatString.Contains("{0")) @@ -657,7 +657,7 @@ namespace Avalonia.Controls return string.Format(NumberFormat, FormatString, Value); } - return Value.ToString(FormatString, NumberFormat); + return Value?.ToString(FormatString, NumberFormat); } /// @@ -665,7 +665,16 @@ namespace Avalonia.Controls /// private void OnIncrement() { - var result = Value + Increment; + decimal result; + if (Value.HasValue) + { + result = Value.Value + Increment; + } + else + { + result = Minimum; + } + Value = MathUtilities.Clamp(result, Minimum, Maximum); } @@ -674,7 +683,17 @@ namespace Avalonia.Controls /// private void OnDecrement() { - var result = Value - Increment; + decimal result; + + if (Value.HasValue) + { + result = Value.Value - Increment; + } + else + { + result = Maximum; + } + Value = MathUtilities.Clamp(result, Minimum, Maximum); } @@ -688,6 +707,11 @@ namespace Avalonia.Controls // Zero increment always prevents spin. if (Increment != 0 && !IsReadOnly) { + if (!Value.HasValue) + { + validDirections = ValidSpinDirections.Increase | ValidSpinDirections.Decrease; + } + if (Value < Maximum) { validDirections = validDirections | ValidSpinDirections.Increase; @@ -825,13 +849,13 @@ namespace Avalonia.Controls { if (e.Sender is NumericUpDown upDown) { - var oldValue = (decimal)e.OldValue!; - var newValue = (decimal)e.NewValue!; + var oldValue = (decimal?)e.OldValue; + var newValue = (decimal?)e.NewValue; upDown.OnValueChanged(oldValue, newValue); } } - private void SetValueInternal(decimal value) + private void SetValueInternal(decimal? value) { _internalValueSet = true; try @@ -946,9 +970,9 @@ namespace Avalonia.Controls remove { RemoveHandler(ValueChangedEvent, value); } } - private bool CommitInput() + private bool CommitInput(bool forceTextUpdate = false) { - return SyncTextAndValueProperties(true, Text); + return SyncTextAndValueProperties(true, Text, forceTextUpdate); } /// @@ -978,28 +1002,24 @@ namespace Avalonia.Controls { if (updateValueFromText) { - if (!string.IsNullOrEmpty(text)) + try { - try + var newValue = ConvertTextToValue(text); + if (!Equals(newValue, Value)) { - var newValue = ConvertTextToValue(text); - if (!Equals(newValue, Value)) - { - SetValueInternal(newValue); - } - } - catch - { - parsedTextIsValid = false; + SetValueInternal(newValue); } } + catch + { + parsedTextIsValid = false; + } } // Do not touch the ongoing text input from user. if (!_isTextChangedFromUI) { - var keepEmpty = !forceTextUpdate && string.IsNullOrEmpty(Text); - if (!keepEmpty) + if (forceTextUpdate) { var newText = ConvertValueToText(); if (!Equals(Text, newText)) @@ -1036,10 +1056,15 @@ namespace Avalonia.Controls return parsedTextIsValid; } - private decimal ConvertTextToValueCore(string currentValueText, string text) + private decimal? ConvertTextToValueCore(string? currentValueText, string? text) { decimal result; + if (string.IsNullOrEmpty(text)) + { + return null; + } + if (IsPercent(FormatString)) { result = ParsePercent(text, NumberFormat); @@ -1052,7 +1077,7 @@ namespace Avalonia.Controls var shouldThrow = true; // Check if CurrentValueText is also failing => it also contains special characters. ex : 90° - if (!decimal.TryParse(currentValueText, ParsingNumberStyle, NumberFormat, out var _)) + if (!string.IsNullOrEmpty(currentValueText) && !decimal.TryParse(currentValueText, ParsingNumberStyle, NumberFormat, out var _)) { // extract non-digit characters var currentValueTextSpecialCharacters = currentValueText.Where(c => !char.IsDigit(c)); @@ -1082,8 +1107,12 @@ namespace Avalonia.Controls return result; } - private void ValidateMinMax(decimal value) + private void ValidateMinMax(decimal? value) { + if (!value.HasValue) + { + return; + } if (value < Minimum) { throw new ArgumentOutOfRangeException(nameof(value), string.Format("Value must be greater than Minimum value of {0}", Minimum)); diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs index 9b467d682c..af835541ae 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs @@ -4,13 +4,13 @@ namespace Avalonia.Controls { public class NumericUpDownValueChangedEventArgs : RoutedEventArgs { - public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, decimal oldValue, decimal newValue) : base(routedEvent) + public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, decimal? oldValue, decimal? newValue) : base(routedEvent) { OldValue = oldValue; NewValue = newValue; } - public decimal OldValue { get; } - public decimal NewValue { get; } + public decimal? OldValue { get; } + public decimal? NewValue { get; } } } diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 2f9bf0ac06..868cce879a 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Threading; @@ -14,6 +15,7 @@ namespace Avalonia.Controls.Platform /// /// Provides the default keyboard and pointer interaction for menus. /// + [Unstable] public class DefaultMenuInteractionHandler : IMenuInteractionHandler { private readonly bool _isContextMenu; @@ -51,9 +53,10 @@ namespace Avalonia.Controls.Platform Menu.PointerPressed += PointerPressed; Menu.PointerReleased += PointerReleased; Menu.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed); - Menu.AddHandler(Avalonia.Controls.Menu.MenuOpenedEvent, this.MenuOpened); - Menu.AddHandler(MenuItem.PointerEnterItemEvent, PointerEnter); - Menu.AddHandler(MenuItem.PointerLeaveItemEvent, PointerLeave); + Menu.AddHandler(Avalonia.Controls.Menu.MenuOpenedEvent, MenuOpened); + Menu.AddHandler(MenuItem.PointerEnteredItemEvent, PointerEntered); + Menu.AddHandler(MenuItem.PointerExitedItemEvent, PointerExited); + Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved); _root = Menu.VisualRoot; @@ -86,9 +89,10 @@ namespace Avalonia.Controls.Platform Menu.PointerPressed -= PointerPressed; Menu.PointerReleased -= PointerReleased; Menu.RemoveHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed); - Menu.RemoveHandler(Avalonia.Controls.Menu.MenuOpenedEvent, this.MenuOpened); - Menu.RemoveHandler(MenuItem.PointerEnterItemEvent, PointerEnter); - Menu.RemoveHandler(MenuItem.PointerLeaveItemEvent, PointerLeave); + Menu.RemoveHandler(Avalonia.Controls.Menu.MenuOpenedEvent, MenuOpened); + Menu.RemoveHandler(MenuItem.PointerEnteredItemEvent, PointerEntered); + Menu.RemoveHandler(MenuItem.PointerExitedItemEvent, PointerExited); + Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved); if (_root is InputElement inputRoot) { @@ -293,7 +297,7 @@ namespace Avalonia.Controls.Platform e.Handled = true; } - protected internal virtual void PointerEnter(object? sender, PointerEventArgs e) + protected internal virtual void PointerEntered(object? sender, PointerEventArgs e) { var item = GetMenuItem(e.Source as IControl); @@ -338,7 +342,23 @@ namespace Avalonia.Controls.Platform } } - protected internal virtual void PointerLeave(object? sender, PointerEventArgs e) + protected internal virtual void PointerMoved(object? sender, PointerEventArgs e) + { + // HACK: #8179 needs to be addressed to correctly implement it in the PointerPressed method. + var item = GetMenuItem(e.Source as IControl) as MenuItem; + if (item?.TransformedBounds == null) + { + return; + } + var point = e.GetCurrentPoint(null); + + if (point.Properties.IsLeftButtonPressed && item.TransformedBounds.Value.Contains(point.Position) == false) + { + e.Pointer.Capture(null); + } + } + + protected internal virtual void PointerExited(object? sender, PointerEventArgs e) { var item = GetMenuItem(e.Source as IControl); diff --git a/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs b/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs index a8d3a3b3ac..99bbb8b56d 100644 --- a/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs +++ b/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { + [Unstable] public interface IApplicationPlatformEvents { void RaiseUrlsOpened(string[] urls); diff --git a/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs index dd8503f768..47b5a048b0 100644 --- a/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Controls.Platform +using Avalonia.Metadata; + +namespace Avalonia.Controls.Platform { /// /// Handles user interaction for menus. /// + [Unstable] public interface IMenuInteractionHandler { /// diff --git a/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs b/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs index 6e10163175..daeb9076e6 100644 --- a/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs +++ b/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs @@ -1,13 +1,13 @@ using System; using System.Collections.ObjectModel; -using System.Threading.Tasks; -using Avalonia.Platform; +using Avalonia.Metadata; namespace Avalonia.Controls.Platform { /// /// Defines a platform-specific mount volumes info provider implementation. /// + [Unstable] public interface IMountedVolumeInfoProvider { /// diff --git a/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs b/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs index df13613848..ffa79aa8d6 100644 --- a/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs +++ b/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs @@ -1,10 +1,11 @@ using System; using System.Diagnostics.CodeAnalysis; +using Avalonia.Metadata; using Avalonia.Platform; -using Avalonia.VisualTree; namespace Avalonia.Controls.Platform { + [Unstable] public interface INativeControlHostImpl { INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent); @@ -13,11 +14,13 @@ namespace Avalonia.Controls.Platform bool IsCompatibleWith(IPlatformHandle handle); } + [Unstable] public interface INativeControlHostDestroyableControlHandle : IPlatformHandle { void Destroy(); } + [Unstable] public interface INativeControlHostControlTopLevelAttachment : IDisposable { INativeControlHostImpl? AttachedTo { get; set; } @@ -27,6 +30,7 @@ namespace Avalonia.Controls.Platform void ShowInBounds(Rect rect); } + [Unstable] public interface ITopLevelImplWithNativeControlHost { INativeControlHostImpl? NativeControlHost { get; } diff --git a/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs b/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs index ecbc6d2234..4c844ce30f 100644 --- a/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs +++ b/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs @@ -1,7 +1,9 @@ using System.IO; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformIconLoader { IWindowIconImpl LoadIcon(string fileName); diff --git a/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs b/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs index 4cd6640453..0658f9211c 100644 --- a/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs +++ b/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs @@ -1,9 +1,10 @@ using System; -using System.ComponentModel; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformLifetimeEventsImpl { /// diff --git a/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs b/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs index 264f5e4667..6ad07b1b13 100644 --- a/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs +++ b/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformNativeSurfaceHandle : IPlatformHandle { PixelSize Size { get; } diff --git a/src/Avalonia.Controls/Platform/IPopupImpl.cs b/src/Avalonia.Controls/Platform/IPopupImpl.cs index 477d5fab43..cd86045dee 100644 --- a/src/Avalonia.Controls/Platform/IPopupImpl.cs +++ b/src/Avalonia.Controls/Platform/IPopupImpl.cs @@ -1,10 +1,12 @@ using Avalonia.Controls.Primitives.PopupPositioning; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines a platform-specific popup window implementation. /// + [Unstable] public interface IPopupImpl : IWindowBaseImpl { IPopupPositioner PopupPositioner { get; } diff --git a/src/Avalonia.Controls/Platform/IScreenImpl.cs b/src/Avalonia.Controls/Platform/IScreenImpl.cs index b68391aa52..fcae3b6493 100644 --- a/src/Avalonia.Controls/Platform/IScreenImpl.cs +++ b/src/Avalonia.Controls/Platform/IScreenImpl.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IScreenImpl { int ScreenCount { get; } diff --git a/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs b/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs index 1685a6a38c..715eda5cfa 100644 --- a/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs +++ b/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs @@ -1,10 +1,12 @@ using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Controls.Platform { /// /// Defines a platform-specific system dialog implementation. /// + [Unstable] public interface ISystemDialogImpl { /// diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index 80434882f7..bd0339f525 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -4,6 +4,7 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Layout; +using Avalonia.Metadata; using Avalonia.Rendering; using JetBrains.Annotations; @@ -50,6 +51,7 @@ namespace Avalonia.Platform /// This interface is the common interface to and /// . /// + [Unstable] public interface ITopLevelImpl : IDisposable { /// diff --git a/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs b/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs index bafb973765..a2e426ca08 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs @@ -1,9 +1,11 @@ using Avalonia.Input; using Avalonia.Input.TextInput; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Controls.Platform { + [Unstable] public interface ITopLevelImplWithTextInputMethod : ITopLevelImpl { public ITextInputMethodImpl? TextInputMethod { get; } diff --git a/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs b/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs index 9e72a40439..149a978c54 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs @@ -1,13 +1,16 @@ using System; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Controls.Platform { + [Unstable] public interface INativeMenuExporter { void SetNativeMenu(NativeMenu? menu); } + [Unstable] public interface ITopLevelNativeMenuExporter : INativeMenuExporter { bool IsNativeMenuExported { get; } @@ -15,11 +18,13 @@ namespace Avalonia.Controls.Platform event EventHandler OnIsNativeMenuExportedChanged; } + [Unstable] public interface INativeMenuExporterProvider { INativeMenuExporter? NativeMenuExporter { get; } } - + + [Unstable] public interface ITopLevelImplWithNativeMenuExporter : ITopLevelImpl { ITopLevelNativeMenuExporter? NativeMenuExporter { get; } diff --git a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs index 289b569645..4ef9397d04 100644 --- a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs +++ b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs @@ -1,8 +1,10 @@ using System; using Avalonia.Controls.Platform; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface ITrayIconImpl : IDisposable { /// diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs index 066f4579c0..512fad6dfc 100644 --- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs @@ -1,8 +1,10 @@ using System; using Avalonia.Automation.Peers; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IWindowBaseImpl : ITopLevelImpl { /// diff --git a/src/Avalonia.Controls/Platform/IWindowIconImpl.cs b/src/Avalonia.Controls/Platform/IWindowIconImpl.cs index 7086b7651c..4bb8844d97 100644 --- a/src/Avalonia.Controls/Platform/IWindowIconImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowIconImpl.cs @@ -1,7 +1,9 @@ using System.IO; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IWindowIconImpl { void Save(Stream outputStream); diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index d4be4f9f45..af9392d440 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -1,12 +1,14 @@ using System; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines a platform-specific window implementation. /// + [Unstable] public interface IWindowImpl : IWindowBaseImpl { /// diff --git a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs index fa26fe8fdd..5acc5adccd 100644 --- a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs +++ b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { + [Unstable] public interface IWindowingPlatform { IWindowImpl CreateWindow(); diff --git a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs index 4e5908456e..630d2d8efb 100644 --- a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs +++ b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; -using System.Threading.Tasks; +using Avalonia.Metadata; using Avalonia.Platform; -using Avalonia.Rendering; using Avalonia.Threading; namespace Avalonia.Controls.Platform { + [Unstable] public class InternalPlatformThreadingInterface : IPlatformThreadingInterface { public InternalPlatformThreadingInterface() diff --git a/src/Avalonia.Controls/Platform/MountedDriveInfo.cs b/src/Avalonia.Controls/Platform/MountedDriveInfo.cs index f3104e4360..620ac9303f 100644 --- a/src/Avalonia.Controls/Platform/MountedDriveInfo.cs +++ b/src/Avalonia.Controls/Platform/MountedDriveInfo.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Controls.Platform { /// /// Describes a Drive's properties. /// + [Unstable] public class MountedVolumeInfo : IEquatable { public string? VolumeLabel { get; set; } diff --git a/src/Avalonia.Controls/Platform/PlatformManager.cs b/src/Avalonia.Controls/Platform/PlatformManager.cs index ee62316922..92f6f1cb52 100644 --- a/src/Avalonia.Controls/Platform/PlatformManager.cs +++ b/src/Avalonia.Controls/Platform/PlatformManager.cs @@ -1,9 +1,11 @@ using System; using System.Reactive.Disposables; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Controls.Platform { + [Unstable] public static partial class PlatformManager { static bool s_designerMode; diff --git a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs index 62cd012d51..0a7daeaa24 100644 --- a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs +++ b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs @@ -1,7 +1,9 @@ -using Avalonia.Platform; +using Avalonia.Metadata; +using Avalonia.Platform; namespace Avalonia.Controls.Platform.Surfaces { + [Unstable] public interface IFramebufferPlatformSurface { /// diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs index 3d1e7eb5a8..87bbfce2a2 100644 --- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs +++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs @@ -31,7 +31,7 @@ namespace Avalonia.Controls.Presenters /// /// Defines the property. /// - public static readonly StyledProperty PageTransitionProperty = + public static readonly StyledProperty PageTransitionProperty = Carousel.PageTransitionProperty.AddOwner(); private int _selectedIndex = -1; @@ -85,7 +85,7 @@ namespace Avalonia.Controls.Presenters /// /// Gets or sets a transition to use when switching pages. /// - public IPageTransition PageTransition + public IPageTransition? PageTransition { get { return GetValue(PageTransitionProperty); } set { SetValue(PageTransitionProperty, value); } diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index c67678837b..39888c1239 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -52,67 +52,67 @@ namespace Avalonia.Controls.Presenters /// /// Defines the property. /// - public static readonly AttachedProperty ForegroundProperty = + public static readonly StyledProperty ForegroundProperty = TextElement.ForegroundProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontFamilyProperty = + public static readonly StyledProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontSizeProperty = + public static readonly StyledProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontStyleProperty = + public static readonly StyledProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontWeightProperty = + public static readonly StyledProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontStretchProperty = + public static readonly StyledProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextAlignmentProperty = + public static readonly StyledProperty TextAlignmentProperty = TextBlock.TextAlignmentProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextWrappingProperty = + public static readonly StyledProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextTrimmingProperty = + public static readonly StyledProperty TextTrimmingProperty = TextBlock.TextTrimmingProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty LineHeightProperty = + public static readonly StyledProperty LineHeightProperty = TextBlock.LineHeightProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty MaxLinesProperty = + public static readonly StyledProperty MaxLinesProperty = TextBlock.MaxLinesProperty.AddOwner(); /// diff --git a/src/Avalonia.Controls/Presenters/IContentPresenter.cs b/src/Avalonia.Controls/Presenters/IContentPresenter.cs index ab4d61e3bd..673de4700b 100644 --- a/src/Avalonia.Controls/Presenters/IContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IContentPresenter.cs @@ -1,4 +1,5 @@ using Avalonia.Controls.Primitives; +using Avalonia.Metadata; namespace Avalonia.Controls.Presenters { @@ -6,6 +7,7 @@ namespace Avalonia.Controls.Presenters /// Interface for controls that present a single item of data inside a /// template. /// + [NotClientImplementable] public interface IContentPresenter : IPresenter { /// diff --git a/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs b/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs index 78c4affe44..562638e94a 100644 --- a/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs +++ b/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs @@ -1,5 +1,6 @@ using Avalonia.Collections; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Styling; namespace Avalonia.Controls.Presenters @@ -15,6 +16,7 @@ namespace Avalonia.Controls.Presenters /// parent control's template is instantiated so they register themselves using this /// interface. /// + [NotClientImplementable] public interface IContentPresenterHost : ITemplatedControl { /// diff --git a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs index e7da3d4618..7cc72ef0a7 100644 --- a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs @@ -1,8 +1,10 @@ using System.Collections; using System.Collections.Specialized; +using Avalonia.Metadata; namespace Avalonia.Controls.Presenters { + [NotClientImplementable] public interface IItemsPresenter : IPresenter { IEnumerable? Items { get; set; } diff --git a/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs b/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs index ba9ee0fe31..db11474871 100644 --- a/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs +++ b/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; using Avalonia.Styling; namespace Avalonia.Controls.Presenters @@ -13,6 +14,7 @@ namespace Avalonia.Controls.Presenters /// parent control's template is instantiated so they register themselves using this /// interface. /// + [NotClientImplementable] public interface IItemsPresenterHost : ITemplatedControl { /// diff --git a/src/Avalonia.Controls/Presenters/IPresenter.cs b/src/Avalonia.Controls/Presenters/IPresenter.cs index 5318ea2757..0399983189 100644 --- a/src/Avalonia.Controls/Presenters/IPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IPresenter.cs @@ -1,4 +1,5 @@ using Avalonia.Controls.Primitives; +using Avalonia.Metadata; namespace Avalonia.Controls.Presenters { @@ -12,6 +13,7 @@ namespace Avalonia.Controls.Presenters /// of a then that signals that the visual child /// of the presenter is not a part of the template. /// + [NotClientImplementable] public interface IPresenter : IControl, INamed { } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 07061940b5..3523cd5214 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -33,7 +33,7 @@ namespace Avalonia.Controls.Presenters public static readonly StyledProperty CaretBrushProperty = AvaloniaProperty.Register(nameof(CaretBrush)); - + public static readonly DirectProperty SelectionStartProperty = TextBox.SelectionStartProperty.AddOwner( o => o.SelectionStart, @@ -43,7 +43,7 @@ namespace Avalonia.Controls.Presenters TextBox.SelectionEndProperty.AddOwner( o => o.SelectionEnd, (o, v) => o.SelectionEnd = v); - + /// /// Defines the property. /// @@ -65,6 +65,12 @@ namespace Avalonia.Controls.Presenters public static readonly StyledProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner(); + /// + /// Defines the property. + /// + public static readonly StyledProperty LineHeightProperty = + TextBlock.LineHeightProperty.AddOwner(); + /// /// Defines the property. /// @@ -179,6 +185,15 @@ namespace Avalonia.Controls.Presenters get => GetValue(TextWrappingProperty); set => SetValue(TextWrappingProperty, value); } + + /// + /// Gets or sets the line height. By default, this is set to , which determines the appropriate height automatically. + /// + public double LineHeight + { + get => GetValue(LineHeightProperty); + set => SetValue(LineHeightProperty, value); + } /// /// Gets or sets the text alignment. @@ -253,7 +268,7 @@ namespace Avalonia.Controls.Presenters get => GetValue(CaretBrushProperty); set => SetValue(CaretBrushProperty, value); } - + public int SelectionStart { get @@ -281,7 +296,7 @@ namespace Avalonia.Controls.Presenters SetAndRaise(SelectionEndProperty, ref _selectionEnd, value); } } - + protected override bool BypassFlowDirectionPolicies => true; /// @@ -301,7 +316,7 @@ namespace Avalonia.Controls.Presenters var textLayout = new TextLayout(text, typeface, FontSize, foreground, TextAlignment, TextWrapping, maxWidth: maxWidth, maxHeight: maxHeight, textStyleOverrides: textStyleOverrides, - flowDirection: FlowDirection); + flowDirection: FlowDirection, lineHeight: LineHeight); return textLayout; } @@ -515,11 +530,6 @@ namespace Avalonia.Controls.Presenters protected override Size MeasureOverride(Size availableSize) { - if (string.IsNullOrEmpty(Text)) - { - return new Size(); - } - _constraint = availableSize; _textLayout = null; diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs index 8652924f90..bd2aa39621 100644 --- a/src/Avalonia.Controls/Primitives/IPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs @@ -3,6 +3,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Input; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives @@ -15,6 +16,7 @@ namespace Avalonia.Controls.Primitives /// () or an which is created /// on an . /// + [NotClientImplementable] public interface IPopupHost : IDisposable, IFocusScope { /// diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs index 8daf1ac68a..8d35a91e00 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs @@ -45,6 +45,7 @@ Copyright © 2019 Nikita Tsukanov */ using System; +using Avalonia.Metadata; using Avalonia.VisualTree; using Avalonia.Media; @@ -61,6 +62,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning /// requirement that a popup must intersect with or be at least partially adjacent to its parent /// surface. /// + [Unstable] public struct PopupPositionerParameters { private PopupGravity _gravity; @@ -429,6 +431,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning /// managed implementation is provided in for platforms /// on which popups can be arbitrarily positioned. /// + [NotClientImplementable] public interface IPopupPositioner { /// @@ -439,6 +442,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning void Update(PopupPositionerParameters parameters); } + [Unstable] static class PopupPositionerExtensions { public static void ConfigurePosition(ref this PopupPositionerParameters positionerParameters, diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs index e5c3392faf..e524db5444 100644 --- a/src/Avalonia.Controls/Primitives/ScrollBar.cs +++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs @@ -218,9 +218,9 @@ namespace Avalonia.Controls.Primitives } } - protected override void OnPointerEnter(PointerEventArgs e) + protected override void OnPointerEntered(PointerEventArgs e) { - base.OnPointerEnter(e); + base.OnPointerEntered(e); if (AllowAutoHide) { @@ -228,9 +228,9 @@ namespace Avalonia.Controls.Primitives } } - protected override void OnPointerLeave(PointerEventArgs e) + protected override void OnPointerExited(PointerEventArgs e) { - base.OnPointerLeave(e); + base.OnPointerExited(e); if (AllowAutoHide) { diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 164aab32db..ea20247b4b 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -111,14 +111,14 @@ namespace Avalonia.Controls.Primitives /// public static readonly RoutedEvent SelectionChangedEvent = RoutedEvent.Register( - "SelectionChanged", + nameof(SelectionChanged), RoutingStrategies.Bubble); /// /// Defines the property. /// public static readonly StyledProperty WrapSelectionProperty = - AvaloniaProperty.Register(nameof(WrapSelection), defaultValue: false); + AvaloniaProperty.Register(nameof(WrapSelection), defaultValue: false); private static readonly IList Empty = Array.Empty(); private string _textSearchTerm = string.Empty; diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index 6e4ae748d9..db029d38c0 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -97,7 +97,7 @@ namespace Avalonia.Controls.Primitives /// public static readonly RoutedEvent TemplateAppliedEvent = RoutedEvent.Register( - "TemplateApplied", + nameof(TemplateApplied), RoutingStrategies.Direct); private IControlTemplate? _appliedTemplate; diff --git a/src/Avalonia.Controls/Properties/AssemblyInfo.cs b/src/Avalonia.Controls/Properties/AssemblyInfo.cs index 25330614cf..0f3da91107 100644 --- a/src/Avalonia.Controls/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls/Properties/AssemblyInfo.cs @@ -1,10 +1,5 @@ -using System.Runtime.CompilerServices; using Avalonia.Metadata; -[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Automation")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] diff --git a/src/Avalonia.Controls/Remote/RemoteServer.cs b/src/Avalonia.Controls/Remote/RemoteServer.cs index f4cc91a0e6..2cf2d2b97d 100644 --- a/src/Avalonia.Controls/Remote/RemoteServer.cs +++ b/src/Avalonia.Controls/Remote/RemoteServer.cs @@ -1,11 +1,12 @@ using System; using Avalonia.Controls.Embedding; using Avalonia.Controls.Remote.Server; -using Avalonia.Platform; +using Avalonia.Metadata; using Avalonia.Remote.Protocol; namespace Avalonia.Controls.Remote { + [Unstable] public class RemoteServer { private EmbeddableControlRoot _topLevel; diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index c9fd1dc3b8..e800f2f4b0 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -6,6 +6,7 @@ using Avalonia.Controls.Platform.Surfaces; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Layout; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Input; @@ -18,6 +19,7 @@ using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat; namespace Avalonia.Controls.Remote.Server { + [Unstable] public class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface { private readonly IAvaloniaRemoteTransportConnection _transport; diff --git a/src/Avalonia.Controls/RepeatButton.cs b/src/Avalonia.Controls/RepeatButton.cs index 80f841fa18..6a62172934 100644 --- a/src/Avalonia.Controls/RepeatButton.cs +++ b/src/Avalonia.Controls/RepeatButton.cs @@ -13,13 +13,13 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty IntervalProperty = - AvaloniaProperty.Register(nameof(Interval), 100); + AvaloniaProperty.Register(nameof(Interval), 100); /// /// Defines the property. /// public static readonly StyledProperty DelayProperty = - AvaloniaProperty.Register(nameof(Delay), 300); + AvaloniaProperty.Register(nameof(Delay), 300); private DispatcherTimer? _repeatTimer; diff --git a/src/Avalonia.Controls/SystemDialog.cs b/src/Avalonia.Controls/SystemDialog.cs index 4a9e745e30..093f10be51 100644 --- a/src/Avalonia.Controls/SystemDialog.cs +++ b/src/Avalonia.Controls/SystemDialog.cs @@ -15,7 +15,7 @@ namespace Avalonia.Controls /// Gets or sets a collection of filters which determine the types of files displayed in an /// or an . /// - public List Filters { get; set; } = new List(); + public List? Filters { get; set; } = new List(); /// /// Gets or sets initial file name that is displayed when the dialog is opened. diff --git a/src/Avalonia.Controls/Templates/DataTemplates.cs b/src/Avalonia.Controls/Templates/DataTemplates.cs index f203539536..d4eeda7908 100644 --- a/src/Avalonia.Controls/Templates/DataTemplates.cs +++ b/src/Avalonia.Controls/Templates/DataTemplates.cs @@ -1,3 +1,4 @@ +using System; using Avalonia.Collections; namespace Avalonia.Controls.Templates @@ -13,6 +14,22 @@ namespace Avalonia.Controls.Templates public DataTemplates() { ResetBehavior = ResetBehavior.Remove; + + Validate += ValidateDataTemplate; + } + + private static void ValidateDataTemplate(IDataTemplate template) + { + var valid = template switch + { + ITypedDataTemplate typed => typed.DataType is not null, + _ => true + }; + + if (!valid) + { + throw new InvalidOperationException("DataTemplate inside of DataTemplates must have a DataType set. Set DataType property or use ItemTemplate with single template instead."); + } } } } \ No newline at end of file diff --git a/src/Avalonia.Controls/Templates/IDataTemplateHost.cs b/src/Avalonia.Controls/Templates/IDataTemplateHost.cs index 61986a0661..ce763c3336 100644 --- a/src/Avalonia.Controls/Templates/IDataTemplateHost.cs +++ b/src/Avalonia.Controls/Templates/IDataTemplateHost.cs @@ -1,9 +1,11 @@ - +using Avalonia.Metadata; + namespace Avalonia.Controls.Templates { /// /// Defines an element that has a collection. /// + [NotClientImplementable] public interface IDataTemplateHost { /// diff --git a/src/Avalonia.Controls/Templates/ITypedDataTemplate.cs b/src/Avalonia.Controls/Templates/ITypedDataTemplate.cs new file mode 100644 index 0000000000..239dbd79f4 --- /dev/null +++ b/src/Avalonia.Controls/Templates/ITypedDataTemplate.cs @@ -0,0 +1,10 @@ +using System; +using Avalonia.Metadata; + +namespace Avalonia.Controls.Templates; + +public interface ITypedDataTemplate : IDataTemplate +{ + [DataType] + Type? DataType { get; } +} diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index bbe6aeb7ee..db315d3aaf 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -113,7 +113,9 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly AttachedProperty TextAlignmentProperty = - AvaloniaProperty.RegisterAttached(nameof(TextAlignment), + AvaloniaProperty.RegisterAttached( + nameof(TextAlignment), + defaultValue: TextAlignment.Start, inherits: true); /// @@ -631,7 +633,11 @@ namespace Avalonia.Controls return finalSize; } - _constraint = new Size(finalSize.Width, double.PositiveInfinity); + var scale = LayoutHelper.GetLayoutScale(this); + + var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); + + _constraint = new Size(finalSize.Deflate(padding).Width, double.PositiveInfinity); _textLayout = null; @@ -744,14 +750,14 @@ namespace Avalonia.Controls { if (textSourceIndex > _text.Length) { - return null; + return new TextEndOfParagraph(); } var runText = _text.Skip(textSourceIndex); if (runText.IsEmpty) { - return null; + return new TextEndOfParagraph(); } return new TextCharacters(runText, _defaultProperties); diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 45e66828c1..9531f719b9 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -53,7 +53,7 @@ namespace Avalonia.Controls public static readonly StyledProperty PasswordCharProperty = AvaloniaProperty.Register(nameof(PasswordChar)); - + public static readonly StyledProperty SelectionBrushProperty = AvaloniaProperty.Register(nameof(SelectionBrush)); @@ -79,8 +79,8 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(MaxLength), defaultValue: 0); public static readonly StyledProperty MaxLinesProperty = - AvaloniaProperty.Register(nameof(MaxLines), defaultValue: 0); - + AvaloniaProperty.Register(nameof(MaxLines), defaultValue: 0); + public static readonly DirectProperty TextProperty = TextBlock.TextProperty.AddOwnerWithDataValidation( o => o.Text, @@ -105,6 +105,12 @@ namespace Avalonia.Controls public static readonly StyledProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner(); + + /// + /// Defines see property. + /// + public static readonly StyledProperty LineHeightProperty = + TextBlock.LineHeightProperty.AddOwner(); public static readonly StyledProperty WatermarkProperty = AvaloniaProperty.Register(nameof(Watermark)); @@ -154,15 +160,15 @@ namespace Avalonia.Controls public static readonly RoutedEvent CopyingToClipboardEvent = RoutedEvent.Register( - "CopyingToClipboard", RoutingStrategies.Bubble); + nameof(CopyingToClipboard), RoutingStrategies.Bubble); public static readonly RoutedEvent CuttingToClipboardEvent = RoutedEvent.Register( - "CuttingToClipboard", RoutingStrategies.Bubble); + nameof(CuttingToClipboard), RoutingStrategies.Bubble); public static readonly RoutedEvent PastingFromClipboardEvent = RoutedEvent.Register( - "PastingFromClipboard", RoutingStrategies.Bubble); + nameof(PastingFromClipboard), RoutingStrategies.Bubble); readonly struct UndoRedoState : IEquatable { @@ -190,7 +196,6 @@ namespace Avalonia.Controls private TextBoxTextInputMethodClient _imClient = new TextBoxTextInputMethodClient(); private UndoRedoHelper _undoRedoHelper; private bool _isUndoingRedoing; - private bool _ignoreTextChanges; private bool _canCut; private bool _canCopy; private bool _canPaste; @@ -239,23 +244,19 @@ namespace Avalonia.Controls public bool AcceptsReturn { - get { return GetValue(AcceptsReturnProperty); } - set { SetValue(AcceptsReturnProperty, value); } + get => GetValue(AcceptsReturnProperty); + set => SetValue(AcceptsReturnProperty, value); } public bool AcceptsTab { - get { return GetValue(AcceptsTabProperty); } - set { SetValue(AcceptsTabProperty, value); } + get => GetValue(AcceptsTabProperty); + set => SetValue(AcceptsTabProperty, value); } public int CaretIndex { - get - { - return _caretIndex; - } - + get => _caretIndex; set { value = CoerceCaretIndex(value); @@ -271,10 +272,10 @@ namespace Avalonia.Controls public bool IsReadOnly { - get { return GetValue(IsReadOnlyProperty); } - set { SetValue(IsReadOnlyProperty, value); } + get => GetValue(IsReadOnlyProperty); + set => SetValue(IsReadOnlyProperty, value); } - + public char PasswordChar { get => GetValue(PasswordCharProperty); @@ -301,11 +302,7 @@ namespace Avalonia.Controls public int SelectionStart { - get - { - return _selectionStart; - } - + get => _selectionStart; set { value = CoerceCaretIndex(value); @@ -325,11 +322,7 @@ namespace Avalonia.Controls public int SelectionEnd { - get - { - return _selectionEnd; - } - + get => _selectionEnd; set { value = CoerceCaretIndex(value); @@ -349,44 +342,49 @@ namespace Avalonia.Controls public int MaxLength { - get { return GetValue(MaxLengthProperty); } - set { SetValue(MaxLengthProperty, value); } + get => GetValue(MaxLengthProperty); + set => SetValue(MaxLengthProperty, value); } public int MaxLines { - get { return GetValue(MaxLinesProperty); } - set { SetValue(MaxLinesProperty, value); } + get => GetValue(MaxLinesProperty); + set => SetValue(MaxLinesProperty, value); + } + + /// + /// Gets or sets the line height. + /// + public double LineHeight + { + get { return GetValue(LineHeightProperty); } + set { SetValue(LineHeightProperty, value); } } [Content] public string? Text { - get { return _text; } + get => _text; set { - if (!_ignoreTextChanges) - { - var caretIndex = CaretIndex; - var selectionStart = SelectionStart; - var selectionEnd = SelectionEnd; + var caretIndex = CaretIndex; + var selectionStart = SelectionStart; + var selectionEnd = SelectionEnd; - CaretIndex = CoerceCaretIndex(caretIndex, value); - SelectionStart = CoerceCaretIndex(selectionStart, value); - SelectionEnd = CoerceCaretIndex(selectionEnd, value); - - if (SetAndRaise(TextProperty, ref _text, value) && IsUndoEnabled && !_isUndoingRedoing) - { - _undoRedoHelper.Clear(); - SnapshotUndoRedo(); // so we always have an initial state - } + CaretIndex = CoerceCaretIndex(caretIndex, value); + SelectionStart = CoerceCaretIndex(selectionStart, value); + SelectionEnd = CoerceCaretIndex(selectionEnd, value); + if (SetAndRaise(TextProperty, ref _text, value) && IsUndoEnabled && !_isUndoingRedoing) + { + _undoRedoHelper.Clear(); + SnapshotUndoRedo(); // so we always have an initial state } } } public string SelectedText { - get { return GetSelection(); } + get => GetSelection(); set { if (string.IsNullOrEmpty(value)) @@ -407,8 +405,8 @@ namespace Avalonia.Controls /// public HorizontalAlignment HorizontalContentAlignment { - get { return GetValue(HorizontalContentAlignmentProperty); } - set { SetValue(HorizontalContentAlignmentProperty, value); } + get => GetValue(HorizontalContentAlignmentProperty); + set => SetValue(HorizontalContentAlignmentProperty, value); } /// @@ -416,50 +414,58 @@ namespace Avalonia.Controls /// public VerticalAlignment VerticalContentAlignment { - get { return GetValue(VerticalContentAlignmentProperty); } - set { SetValue(VerticalContentAlignmentProperty, value); } + get => GetValue(VerticalContentAlignmentProperty); + set => SetValue(VerticalContentAlignmentProperty, value); } public TextAlignment TextAlignment { - get { return GetValue(TextAlignmentProperty); } - set { SetValue(TextAlignmentProperty, value); } + get => GetValue(TextAlignmentProperty); + set => SetValue(TextAlignmentProperty, value); } + /// + /// Gets or sets the placeholder or descriptive text that is displayed even if the + /// property is not yet set. + /// public string? Watermark { - get { return GetValue(WatermarkProperty); } - set { SetValue(WatermarkProperty, value); } + get => GetValue(WatermarkProperty); + set => SetValue(WatermarkProperty, value); } + /// + /// Gets or sets a value indicating whether the will still be shown above the + /// even after a text value is set. + /// public bool UseFloatingWatermark { - get { return GetValue(UseFloatingWatermarkProperty); } - set { SetValue(UseFloatingWatermarkProperty, value); } + get => GetValue(UseFloatingWatermarkProperty); + set => SetValue(UseFloatingWatermarkProperty, value); } public object InnerLeftContent { - get { return GetValue(InnerLeftContentProperty); } - set { SetValue(InnerLeftContentProperty, value); } + get => GetValue(InnerLeftContentProperty); + set => SetValue(InnerLeftContentProperty, value); } public object InnerRightContent { - get { return GetValue(InnerRightContentProperty); } - set { SetValue(InnerRightContentProperty, value); } + get => GetValue(InnerRightContentProperty); + set => SetValue(InnerRightContentProperty, value); } public bool RevealPassword { - get { return GetValue(RevealPasswordProperty); } - set { SetValue(RevealPasswordProperty, value); } + get => GetValue(RevealPasswordProperty); + set => SetValue(RevealPasswordProperty, value); } public TextWrapping TextWrapping { - get { return GetValue(TextWrappingProperty); } - set { SetValue(TextWrappingProperty, value); } + get => GetValue(TextWrappingProperty); + set => SetValue(TextWrappingProperty, value); } /// @@ -467,8 +473,8 @@ namespace Avalonia.Controls /// public string NewLine { - get { return _newLine; } - set { SetAndRaise(NewLineProperty, ref _newLine, value); } + get => _newLine; + set => SetAndRaise(NewLineProperty, ref _newLine, value); } /// @@ -484,8 +490,8 @@ namespace Avalonia.Controls /// public bool CanCut { - get { return _canCut; } - private set { SetAndRaise(CanCutProperty, ref _canCut, value); } + get => _canCut; + private set => SetAndRaise(CanCutProperty, ref _canCut, value); } /// @@ -493,8 +499,8 @@ namespace Avalonia.Controls /// public bool CanCopy { - get { return _canCopy; } - private set { SetAndRaise(CanCopyProperty, ref _canCopy, value); } + get => _canCopy; + private set => SetAndRaise(CanCopyProperty, ref _canCopy, value); } /// @@ -502,8 +508,8 @@ namespace Avalonia.Controls /// public bool CanPaste { - get { return _canPaste; } - private set { SetAndRaise(CanPasteProperty, ref _canPaste, value); } + get => _canPaste; + private set => SetAndRaise(CanPasteProperty, ref _canPaste, value); } /// @@ -511,13 +517,13 @@ namespace Avalonia.Controls /// public bool IsUndoEnabled { - get { return GetValue(IsUndoEnabledProperty); } - set { SetValue(IsUndoEnabledProperty, value); } + get => GetValue(IsUndoEnabledProperty); + set => SetValue(IsUndoEnabledProperty, value); } public int UndoLimit { - get { return _undoRedoHelper.Limit; } + get => _undoRedoHelper.Limit; set { if (_undoRedoHelper.Limit != value) @@ -725,32 +731,23 @@ namespace Avalonia.Controls { var oldText = _text; - _ignoreTextChanges = true; - - try - { - DeleteSelection(false); - var caretIndex = CaretIndex; - text = Text ?? string.Empty; - SetTextInternal(text.Substring(0, caretIndex) + input + text.Substring(caretIndex)); - ClearSelection(); - - if (IsUndoEnabled) - { - _undoRedoHelper.DiscardRedo(); - } - - if (_text != oldText) - { - RaisePropertyChanged(TextProperty, oldText, _text); - } + DeleteSelection(false); + var caretIndex = CaretIndex; + text = Text ?? string.Empty; + SetTextInternal(text.Substring(0, caretIndex) + input + text.Substring(caretIndex)); + ClearSelection(); - CaretIndex = caretIndex + input.Length; + if (IsUndoEnabled) + { + _undoRedoHelper.DiscardRedo(); } - finally + + if (_text != oldText) { - _ignoreTextChanges = false; + RaisePropertyChanged(TextProperty, oldText, _text); } + + CaretIndex = caretIndex + input.Length; } } @@ -1488,15 +1485,7 @@ namespace Avalonia.Controls { if (raiseTextChanged) { - try - { - _ignoreTextChanges = true; - SetAndRaise(TextProperty, ref _text, value); - } - finally - { - _ignoreTextChanges = false; - } + SetAndRaise(TextProperty, ref _text, value); } else { @@ -1539,7 +1528,7 @@ namespace Avalonia.Controls UndoRedoState UndoRedoHelper.IUndoRedoHost.UndoRedoState { - get { return new UndoRedoState(Text, CaretIndex); } + get => new UndoRedoState(Text, CaretIndex); set { Text = value.Text; diff --git a/src/Avalonia.Controls/ToolTipService.cs b/src/Avalonia.Controls/ToolTipService.cs index a3100ff296..c6efe10488 100644 --- a/src/Avalonia.Controls/ToolTipService.cs +++ b/src/Avalonia.Controls/ToolTipService.cs @@ -26,14 +26,14 @@ namespace Avalonia.Controls if (e.OldValue != null) { - control.PointerEnter -= ControlPointerEnter; - control.PointerLeave -= ControlPointerLeave; + control.PointerEntered -= ControlPointerEntered; + control.PointerExited -= ControlPointerExited; } if (e.NewValue != null) { - control.PointerEnter += ControlPointerEnter; - control.PointerLeave += ControlPointerLeave; + control.PointerEntered += ControlPointerEntered; + control.PointerExited += ControlPointerExited; } if (ToolTip.GetIsOpen(control) && e.NewValue != e.OldValue && !(e.NewValue is ToolTip)) @@ -80,7 +80,7 @@ namespace Avalonia.Controls /// /// The event sender. /// The event args. - private void ControlPointerEnter(object? sender, PointerEventArgs e) + private void ControlPointerEntered(object? sender, PointerEventArgs e) { StopTimer(); @@ -101,7 +101,7 @@ namespace Avalonia.Controls /// /// The event sender. /// The event args. - private void ControlPointerLeave(object? sender, PointerEventArgs e) + private void ControlPointerExited(object? sender, PointerEventArgs e) { var control = (Control)sender!; Close(control); diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 33a05f126d..aabfd3ef18 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -1,4 +1,5 @@ using Avalonia.Media; +using Avalonia.Media.Immutable; using Avalonia.Metadata; namespace Avalonia.Controls @@ -8,7 +9,7 @@ namespace Avalonia.Controls /// public class Viewbox : Control { - private Decorator _containerVisual; + private readonly ViewboxContainer _containerVisual; /// /// Defines the property. @@ -37,9 +38,10 @@ namespace Avalonia.Controls public Viewbox() { - _containerVisual = new Decorator(); + // The Child control is hosted inside a ViewboxContainer control so that the transform + // can be applied independently of the Viewbox and Child transforms. + _containerVisual = new ViewboxContainer(); _containerVisual.RenderTransformOrigin = RelativePoint.TopLeft; - LogicalChildren.Add(_containerVisual); VisualChildren.Add(_containerVisual); } @@ -88,7 +90,22 @@ namespace Avalonia.Controls if (change.Property == ChildProperty) { - _containerVisual.Child = change.GetNewValue(); + var (oldChild, newChild) = change.GetOldAndNewValue(); + + if (oldChild is not null) + { + ((ISetLogicalParent)oldChild).SetParent(null); + LogicalChildren.Remove(oldChild); + } + + _containerVisual.Child = newChild; + + if (newChild is not null) + { + ((ISetLogicalParent)newChild).SetParent(this); + LogicalChildren.Add(newChild); + } + InvalidateMeasure(); } } @@ -120,7 +137,7 @@ namespace Avalonia.Controls var childSize = child.DesiredSize; var scale = Stretch.CalculateScaling(finalSize, childSize, StretchDirection); - InternalTransform = new ScaleTransform(scale.X, scale.Y); + InternalTransform = new ImmutableTransform(Matrix.CreateScale(scale.X, scale.Y)); child.Arrange(new Rect(childSize)); @@ -129,5 +146,33 @@ namespace Avalonia.Controls return finalSize; } + + /// + /// A simple container control which hosts its child as a visual but not logical child. + /// + private class ViewboxContainer : Control + { + private IControl? _child; + + public IControl? Child + { + get => _child; + set + { + if (_child != value) + { + if (_child is not null) + VisualChildren.Remove(_child); + + _child = value; + + if (_child is not null) + VisualChildren.Add(_child); + + InvalidateMeasure(); + } + } + } + } } } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index a5f99918b2..a4f4534b88 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -171,13 +171,13 @@ namespace Avalonia.Controls /// /// Routed event that can be used for global tracking of window destruction /// - public static readonly RoutedEvent WindowClosedEvent = + public static readonly RoutedEvent WindowClosedEvent = RoutedEvent.Register("WindowClosed", RoutingStrategies.Direct); /// /// Routed event that can be used for global tracking of opening windows /// - public static readonly RoutedEvent WindowOpenedEvent = + public static readonly RoutedEvent WindowOpenedEvent = RoutedEvent.Register("WindowOpened", RoutingStrategies.Direct); @@ -917,6 +917,15 @@ namespace Avalonia.Controls var constraint = clientSize; var maxAutoSize = PlatformImpl?.MaxAutoSizeHint ?? Size.Infinity; + if (MaxWidth > 0 && MaxWidth < maxAutoSize.Width) + { + maxAutoSize = maxAutoSize.WithWidth(MaxWidth); + } + if (MaxHeight > 0 && MaxHeight < maxAutoSize.Height) + { + maxAutoSize = maxAutoSize.WithHeight(MaxHeight); + } + if (sizeToContent.HasAllFlags(SizeToContent.Width)) { constraint = constraint.WithWidth(maxAutoSize.Width); diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 12ba143c8a..5d3e51b394 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -169,7 +169,6 @@ namespace Avalonia.Controls } } - [Obsolete("No longer used. Has no effect.")] protected IDisposable BeginAutoSizing() => Disposable.Empty; @@ -186,6 +185,26 @@ namespace Avalonia.Controls } } + /// + protected override void OnClosed(EventArgs e) + { + // Window must manually raise Loaded/Unloaded events as it is a visual root and + // does not raise OnAttachedToVisualTreeCore/OnDetachedFromVisualTreeCore events + OnUnloadedCore(); + + base.OnClosed(e); + } + + /// + protected override void OnOpened(EventArgs e) + { + // Window must manually raise Loaded/Unloaded events as it is a visual root and + // does not raise OnAttachedToVisualTreeCore/OnDetachedFromVisualTreeCore events + ScheduleOnLoadedCore(); + + base.OnOpened(e); + } + protected override void HandleClosed() { _ignoreVisibilityChange = true; diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index 0270000d8c..0be695e0a1 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -18,7 +18,6 @@ - diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index a1fd425571..e383c160e3 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -60,7 +60,8 @@ namespace Avalonia.Diagnostics.ViewModels var styleDiagnostics = styledElement.GetStyleDiagnostics(); - foreach (var appliedStyle in styleDiagnostics.AppliedStyles) + // We need to place styles without activator first, such styles will be overwritten by ones with activators. + foreach (var appliedStyle in styleDiagnostics.AppliedStyles.OrderBy(s => s.HasActivator)) { var styleSource = appliedStyle.Source; diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs index 58807b489e..587898ac6e 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs @@ -121,8 +121,8 @@ namespace Avalonia.Diagnostics.Views if (header != null) { - header.PointerEnter += AddAdorner; - header.PointerLeave += RemoveAdorner; + header.PointerEntered += AddAdorner; + header.PointerExited += RemoveAdorner; } item.TemplateApplied -= TreeViewItemTemplateApplied; diff --git a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj index a311efdfb0..80159c82d7 100644 --- a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj +++ b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj @@ -14,6 +14,10 @@ + + + + diff --git a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs index 1970c5557d..e4025453c4 100644 --- a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs +++ b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs @@ -8,7 +8,7 @@ namespace Avalonia.Dialogs { public static class ManagedFileDialogExtensions { - private class ManagedSystemDialogImpl : ISystemDialogImpl where T : Window, new() + internal class ManagedSystemDialogImpl : ISystemDialogImpl where T : Window, new() { async Task Show(SystemDialog d, Window parent, ManagedFileDialogOptions options = null) { @@ -141,7 +141,7 @@ namespace Avalonia.Dialogs public static Task ShowManagedAsync(this OpenFileDialog dialog, Window parent, ManagedFileDialogOptions options = null) => ShowManagedAsync(dialog, parent, options); - + public static Task ShowManagedAsync(this OpenFileDialog dialog, Window parent, ManagedFileDialogOptions options = null) where TWindow : Window, new() { diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index bcb63783a4..a5cb207223 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -2,11 +2,15 @@ net6.0;netstandard2.0 + enable + - + + + diff --git a/src/Avalonia.FreeDesktop/DBusFileChooser.cs b/src/Avalonia.FreeDesktop/DBusFileChooser.cs new file mode 100644 index 0000000000..24db614a02 --- /dev/null +++ b/src/Avalonia.FreeDesktop/DBusFileChooser.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Tmds.DBus; + +[assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)] +namespace Avalonia.FreeDesktop +{ + [DBusInterface("org.freedesktop.portal.FileChooser")] + internal interface IFileChooser : IDBusObject + { + Task OpenFileAsync(string ParentWindow, string Title, IDictionary Options); + Task SaveFileAsync(string ParentWindow, string Title, IDictionary Options); + Task SaveFilesAsync(string ParentWindow, string Title, IDictionary Options); + Task GetAsync(string prop); + Task GetAllAsync(); + Task SetAsync(string prop, object val); + Task WatchPropertiesAsync(Action handler); + } + + [Dictionary] + internal class FileChooserProperties + { + public uint Version { get; set; } + } + + internal static class FileChooserExtensions + { + public static Task GetVersionAsync(this IFileChooser o) => o.GetAsync("version"); + } +} diff --git a/src/Avalonia.FreeDesktop/DBusHelper.cs b/src/Avalonia.FreeDesktop/DBusHelper.cs index c14539d7bf..7204e51dbd 100644 --- a/src/Avalonia.FreeDesktop/DBusHelper.cs +++ b/src/Avalonia.FreeDesktop/DBusHelper.cs @@ -6,7 +6,7 @@ using Tmds.DBus; namespace Avalonia.FreeDesktop { - public class DBusHelper + public static class DBusHelper { /// /// This class uses synchronous execution at DBus connection establishment stage @@ -14,14 +14,14 @@ namespace Avalonia.FreeDesktop /// private class DBusSyncContext : SynchronizationContext { - private SynchronizationContext _ctx; - private object _lock = new object(); + private readonly object _lock = new(); + private SynchronizationContext? _ctx; public override void Post(SendOrPostCallback d, object state) { lock (_lock) { - if (_ctx != null) + if (_ctx is not null) _ctx?.Post(d, state); else lock (_lock) @@ -33,10 +33,9 @@ namespace Avalonia.FreeDesktop { lock (_lock) { - if (_ctx != null) + if (_ctx is not null) _ctx?.Send(d, state); else - d(state); } } @@ -47,15 +46,14 @@ namespace Avalonia.FreeDesktop _ctx = new AvaloniaSynchronizationContext(); } } - public static Connection Connection { get; private set; } - public static Connection TryInitialize(string dbusAddress = null) + public static Connection? Connection { get; private set; } + + public static Connection? TryInitialize(string? dbusAddress = null) + => Connection ?? TryCreateNewConnection(dbusAddress); + + public static Connection? TryCreateNewConnection(string? dbusAddress = null) { - return Connection ?? TryCreateNewConnection(dbusAddress); - } - - public static Connection TryCreateNewConnection(string dbusAddress = null) - { var oldContext = SynchronizationContext.Current; try { diff --git a/src/Avalonia.FreeDesktop/DBusRequest.cs b/src/Avalonia.FreeDesktop/DBusRequest.cs new file mode 100644 index 0000000000..940a476916 --- /dev/null +++ b/src/Avalonia.FreeDesktop/DBusRequest.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Tmds.DBus; + +[assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)] +namespace Avalonia.FreeDesktop +{ + [DBusInterface("org.freedesktop.portal.Request")] + internal interface IRequest : IDBusObject + { + Task CloseAsync(); + Task WatchResponseAsync(Action<(uint response, IDictionary results)> handler, Action onError = null); + } +} diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs new file mode 100644 index 0000000000..d1905a4569 --- /dev/null +++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Avalonia.Logging; +using Tmds.DBus; + +namespace Avalonia.FreeDesktop +{ + internal class DBusSystemDialog : ISystemDialogImpl + { + private readonly IFileChooser _fileChooser; + + internal static DBusSystemDialog? TryCreate() + { + var fileChooser = DBusHelper.Connection?.CreateProxy("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop"); + if (fileChooser is null) + return null; + try + { + fileChooser.GetVersionAsync().GetAwaiter().GetResult(); + return new DBusSystemDialog(fileChooser); + } + catch (Exception e) + { + Logger.TryGet(LogEventLevel.Error, LogArea.X11Platform)?.Log(null, $"Unable to connect to org.freedesktop.portal.Desktop: {e.Message}"); + return null; + } + } + + private DBusSystemDialog(IFileChooser fileChooser) + { + _fileChooser = fileChooser; + } + + public async Task ShowFileDialogAsync(FileDialog dialog, Window parent) + { + var parentWindow = $"x11:{parent.PlatformImpl!.Handle.Handle.ToString("X")}"; + ObjectPath objectPath; + var options = new Dictionary(); + if (dialog.Filters is not null) + options.Add("filters", ParseFilters(dialog)); + + switch (dialog) + { + case OpenFileDialog openFileDialog: + options.Add("multiple", openFileDialog.AllowMultiple); + objectPath = await _fileChooser.OpenFileAsync(parentWindow, openFileDialog.Title ?? string.Empty, options); + break; + case SaveFileDialog saveFileDialog: + if (saveFileDialog.InitialFileName is not null) + options.Add("current_name", saveFileDialog.InitialFileName); + if (saveFileDialog.Directory is not null) + options.Add("current_folder", Encoding.UTF8.GetBytes(saveFileDialog.Directory)); + objectPath = await _fileChooser.SaveFileAsync(parentWindow, saveFileDialog.Title ?? string.Empty, options); + break; + } + + var request = DBusHelper.Connection!.CreateProxy("org.freedesktop.portal.Request", objectPath); + var tsc = new TaskCompletionSource(); + using var disposable = await request.WatchResponseAsync(x => tsc.SetResult(x.results["uris"] as string[]), tsc.SetException); + var uris = await tsc.Task; + if (uris is null) + return null; + for (var i = 0; i < uris.Length; i++) + uris[i] = new Uri(uris[i]).AbsolutePath; + return uris; + } + + public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) + { + var parentWindow = $"x11:{parent.PlatformImpl!.Handle.Handle.ToString("X")}"; + var options = new Dictionary + { + { "directory", true } + }; + var objectPath = await _fileChooser.OpenFileAsync(parentWindow, dialog.Title ?? string.Empty, options); + var request = DBusHelper.Connection!.CreateProxy("org.freedesktop.portal.Request", objectPath); + var tsc = new TaskCompletionSource(); + using var disposable = await request.WatchResponseAsync(x => tsc.SetResult(x.results["uris"] as string[]), tsc.SetException); + var uris = await tsc.Task; + if (uris is null) + return null; + return uris.Length != 1 ? string.Empty : new Uri(uris[0]).AbsolutePath; + } + + private static (string name, (uint style, string extension)[])[] ParseFilters(FileDialog dialog) + { + var filters = new (string name, (uint style, string extension)[])[dialog.Filters!.Count]; + for (var i = 0; i < filters.Length; i++) + { + var extensions = dialog.Filters[i].Extensions.Select(static x => (0u, x)).ToArray(); + filters[i] = (dialog.Filters[i].Name ?? string.Empty, extensions); + } + + return filters; + } + } +} diff --git a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs index a7cc4f4cc2..4a55212de3 100644 --- a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs +++ b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs @@ -12,10 +12,6 @@ using Tmds.DBus; [assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)] -[assembly: - InternalsVisibleTo( - "Avalonia.X11, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - namespace Avalonia.FreeDesktop { internal class DBusTrayIconImpl : ITrayIconImpl diff --git a/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs b/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs index f9737b461d..39ddd9d769 100644 --- a/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs +++ b/src/Avalonia.FreeDesktop/LinuxMountedVolumeInfoListener.cs @@ -36,6 +36,13 @@ namespace Avalonia.FreeDesktop private string GetSymlinkTarget(string x) => Path.GetFullPath(Path.Combine(DevByLabelDir, NativeMethods.ReadLink(x))); + private string UnescapeString(string input, string regexText, int escapeBase) => + new Regex(regexText).Replace(input, m => Convert.ToChar(Convert.ToByte(m.Groups[1].Value, escapeBase)).ToString()); + + private string UnescapePathFromProcMounts(string input) => UnescapeString(input, @"\\(\d{3})", 8); + + private string UnescapeDeviceLabel(string input) => UnescapeString(input, @"\\x([0-9a-f]{2})", 16); + private void Poll(long _) { var fProcPartitions = File.ReadAllLines(ProcPartitionsDir) @@ -47,14 +54,14 @@ namespace Avalonia.FreeDesktop var fProcMounts = File.ReadAllLines(ProcMountsDir) .Select(x => x.Split(' ')) - .Select(x => (x[0], x[1])) + .Select(x => (x[0], UnescapePathFromProcMounts(x[1]))) .Where(x => !x.Item2.StartsWith("/snap/", StringComparison.InvariantCultureIgnoreCase)); var labelDirEnum = Directory.Exists(DevByLabelDir) ? new DirectoryInfo(DevByLabelDir).GetFiles() : Enumerable.Empty(); var labelDevPathPairs = labelDirEnum - .Select(x => (GetSymlinkTarget(x.FullName), x.Name)); + .Select(x => (GetSymlinkTarget(x.FullName), UnescapeDeviceLabel(x.Name))); var q1 = from mount in fProcMounts join device in fProcPartitions on mount.Item1 equals device.Item2 @@ -64,7 +71,7 @@ namespace Avalonia.FreeDesktop { VolumePath = mount.Item2, VolumeSizeBytes = device.Item1, - VolumeLabel = x.Name + VolumeLabel = x.Item2 }; var mountVolInfos = q1.ToArray(); diff --git a/src/Avalonia.FreeDesktop/NativeMethods.cs b/src/Avalonia.FreeDesktop/NativeMethods.cs index 8bbda98bb2..147955b6a3 100644 --- a/src/Avalonia.FreeDesktop/NativeMethods.cs +++ b/src/Avalonia.FreeDesktop/NativeMethods.cs @@ -14,15 +14,15 @@ namespace Avalonia.FreeDesktop public static string ReadLink(string path) { - var symlinkMaxSize = Encoding.ASCII.GetMaxByteCount(path.Length); + var symlinkSize = Encoding.UTF8.GetByteCount(path); var bufferSize = 4097; // PATH_MAX is (usually?) 4096, but we need to know if the result was truncated - var symlink = ArrayPool.Shared.Rent(symlinkMaxSize + 1); + var symlink = ArrayPool.Shared.Rent(symlinkSize + 1); var buffer = ArrayPool.Shared.Rent(bufferSize); try { - var symlinkSize = Encoding.UTF8.GetBytes(path, 0, path.Length, symlink, 0); + Encoding.UTF8.GetBytes(path, 0, path.Length, symlink, 0); symlink[symlinkSize] = 0; var size = readlink(symlink, buffer, bufferSize); diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index addc248d58..059a9a4e8f 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -114,6 +114,11 @@ namespace Avalonia.Headless return new HeadlessGlyphRunStub(); } + public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun) + { + return new HeadlessGeometryStub(new Rect(glyphRun.Size)); + } + class HeadlessGeometryStub : IGeometryImpl { public HeadlessGeometryStub(Rect bounds) diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs index 083b16c107..22ff8e8f97 100644 --- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -137,7 +137,7 @@ namespace Avalonia.Headless { var typeface = options.Typeface; var fontRenderingEmSize = options.FontRenderingEmSize; - var bidiLevel = options.BidLevel; + var bidiLevel = options.BidiLevel; return new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel); } diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs index 4372829df1..d1d9c17ae3 100644 --- a/src/Avalonia.Native/SystemDialogs.cs +++ b/src/Avalonia.Native/SystemDialogs.cs @@ -30,7 +30,7 @@ namespace Avalonia.Native ofd.Title ?? "", ofd.Directory ?? "", ofd.InitialFileName ?? "", - string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); + string.Join(";", dialog.Filters?.SelectMany(f => f.Extensions) ?? Array.Empty())); } else { @@ -39,7 +39,7 @@ namespace Avalonia.Native dialog.Title ?? "", dialog.Directory ?? "", dialog.InitialFileName ?? "", - string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); + string.Join(";", dialog.Filters?.SelectMany(f => f.Extensions) ?? Array.Empty())); } return events.Task.ContinueWith(t => { events.Dispose(); return t.Result; }); diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 735f11bcd5..94a3a5ed9b 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -116,8 +116,12 @@ namespace Avalonia.Native { if (_native != null) { - var s = _native.FrameSize; - return new Size(s.Width, s.Height); + unsafe + { + var s = new AvnSize { Width = -1, Height = -1 }; + _native.GetFrameSize(&s); + return s.Width < 0 && s.Height < 0 ? null : new Size(s.Width, s.Height); + } } return default; diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index d6ef0f8918..a1c73e1f03 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -504,7 +504,7 @@ interface IAvnWindowBase : IUnknown HRESULT Close(); HRESULT Activate(); HRESULT GetClientSize(AvnSize*ret); - HRESULT GetFrameSize(AvnSize*ret); + HRESULT GetFrameSize(AvnSize*result); HRESULT GetScaling(double*ret); HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize); HRESULT Resize(double width, double height, AvnPlatformResizeReason reason); diff --git a/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs b/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs index aef4f601be..22f0cebf57 100644 --- a/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs +++ b/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs @@ -1,15 +1,17 @@ using System; -using Avalonia.Media.Imaging; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.OpenGL.Imaging { + [Unstable] public interface IOpenGlBitmapImpl : IBitmapImpl { IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context, Action presentCallback); bool SupportsContext(IGlContext context); } + [Unstable] public interface IOpenGlBitmapAttachment : IDisposable { void Present(); diff --git a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj deleted file mode 100644 index 5336f1e630..0000000000 --- a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net6.0;net461;netstandard2.0 - - - - - - - - - - - - - - - - - - - - diff --git a/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs b/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs deleted file mode 100644 index 4eeb9232cf..0000000000 --- a/src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs +++ /dev/null @@ -1,218 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; -using Avalonia.Platform; - -namespace Avalonia.PlatformSupport -{ - public class StandardRuntimePlatform : IRuntimePlatform - { - public IDisposable StartSystemTimer(TimeSpan interval, Action tick) - { - return new Timer(_ => tick(), null, interval, interval); - } - - public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(this, size); - - private class UnmanagedBlob : IUnmanagedBlob - { - private readonly StandardRuntimePlatform _plat; - private IntPtr _address; - private readonly object _lock = new object(); -#if DEBUG - private static readonly List Backtraces = new List(); - private static Thread? GCThread; - private readonly string _backtrace; - private static readonly object _btlock = new object(); - - class GCThreadDetector - { - ~GCThreadDetector() - { - GCThread = Thread.CurrentThread; - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - static void Spawn() => new GCThreadDetector(); - - static UnmanagedBlob() - { - Spawn(); - GC.WaitForPendingFinalizers(); - } -#endif - - public UnmanagedBlob(StandardRuntimePlatform plat, int size) - { - try - { - if (size <= 0) - throw new ArgumentException("Positive number required", nameof(size)); - _plat = plat; - _address = plat.Alloc(size); - GC.AddMemoryPressure(size); - Size = size; - } - catch - { - GC.SuppressFinalize(this); - throw; - } -#if DEBUG - _backtrace = Environment.StackTrace; - lock (_btlock) - Backtraces.Add(_backtrace); -#endif - } - - void DoDispose() - { - lock (_lock) - { - if (!IsDisposed) - { -#if DEBUG - lock (_btlock) - Backtraces.Remove(_backtrace); -#endif - _plat?.Free(_address, Size); - GC.RemoveMemoryPressure(Size); - IsDisposed = true; - _address = IntPtr.Zero; - Size = 0; - } - } - } - - public void Dispose() - { -#if DEBUG - if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId) - { - lock (_lock) - { - if (!IsDisposed) - { - Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: " - + Environment.StackTrace - + "\n\nBlob created by " + _backtrace); - } - } - } -#endif - DoDispose(); - GC.SuppressFinalize(this); - } - - ~UnmanagedBlob() - { -#if DEBUG - Console.Error.WriteLine("Undisposed native blob created by " + _backtrace); -#endif - DoDispose(); - } - - public IntPtr Address => IsDisposed ? throw new ObjectDisposedException("UnmanagedBlob") : _address; - public int Size { get; private set; } - public bool IsDisposed { get; private set; } - } - -#if NET461 || NETCOREAPP2_0_OR_GREATER - [DllImport("libc", SetLastError = true)] - private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset); - [DllImport("libc", SetLastError = true)] - private static extern int munmap(IntPtr addr, IntPtr length); - [DllImport("libc", SetLastError = true)] - private static extern long sysconf(int name); - - private bool? _useMmap; - private bool UseMmap - => _useMmap ?? ((_useMmap = GetRuntimeInfo().OperatingSystem == OperatingSystemType.Linux)).Value; - - IntPtr Alloc(int size) - { - if (UseMmap) - { - var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero); - if (rv.ToInt64() == -1 || (ulong)rv.ToInt64() == 0xffffffff) - { - var errno = Marshal.GetLastWin32Error(); - throw new Exception("Unable to allocate memory: " + errno); - } - return rv; - } - else - return Marshal.AllocHGlobal(size); - } - - void Free(IntPtr ptr, int len) - { - if (UseMmap) - { - if (munmap(ptr, new IntPtr(len)) == -1) - { - var errno = Marshal.GetLastWin32Error(); - throw new Exception("Unable to free memory: " + errno); - } - } - else - Marshal.FreeHGlobal(ptr); - } -#else - IntPtr Alloc(int size) => Marshal.AllocHGlobal(size); - void Free(IntPtr ptr, int len) => Marshal.FreeHGlobal(ptr); -#endif - - private static readonly Lazy Info = new Lazy(() => - { - OperatingSystemType os; - -#if NET5_0_OR_GREATER - if (OperatingSystem.IsWindows()) - os = OperatingSystemType.WinNT; - else if (OperatingSystem.IsMacOS()) - os = OperatingSystemType.OSX; - else if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) - os = OperatingSystemType.Linux; - else if (OperatingSystem.IsAndroid()) - os = OperatingSystemType.Android; - else if (OperatingSystem.IsIOS()) - os = OperatingSystemType.iOS; - else if (OperatingSystem.IsBrowser()) - os = OperatingSystemType.Browser; - else - throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); -#else - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - os = OperatingSystemType.OSX; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - os = OperatingSystemType.Linux; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - os = OperatingSystemType.WinNT; - else - throw new Exception("Unknown OS platform " + RuntimeInformation.OSDescription); -#endif - - return new RuntimePlatformInfo - { -#if NETCOREAPP - IsCoreClr = true, -#elif NETFRAMEWORK - IsDotNetFramework = true, -#endif - IsDesktop = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.WinNT, - IsMono = os == OperatingSystemType.Android || os == OperatingSystemType.iOS || os == OperatingSystemType.Browser, - IsMobile = os == OperatingSystemType.Android || os == OperatingSystemType.iOS, - IsUnix = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.Android, - IsBrowser = os == OperatingSystemType.Browser, - OperatingSystem = os, - }; - }); - - - public virtual RuntimePlatformInfo GetRuntimeInfo() => Info.Value; - } -} diff --git a/src/Avalonia.Themes.Default/Controls/TextBox.xaml b/src/Avalonia.Themes.Default/Controls/TextBox.xaml index 6e3898a828..1885d3b65b 100644 --- a/src/Avalonia.Themes.Default/Controls/TextBox.xaml +++ b/src/Avalonia.Themes.Default/Controls/TextBox.xaml @@ -76,6 +76,7 @@ SelectionEnd="{TemplateBinding SelectionEnd}" TextAlignment="{TemplateBinding TextAlignment}" TextWrapping="{TemplateBinding TextWrapping}" + LineHeight="{TemplateBinding LineHeight}" PasswordChar="{TemplateBinding PasswordChar}" RevealPassword="{TemplateBinding RevealPassword}" SelectionBrush="{TemplateBinding SelectionBrush}" diff --git a/src/Avalonia.Themes.Default/SimpleTheme.cs b/src/Avalonia.Themes.Default/SimpleTheme.cs index 6929660757..664c95644f 100644 --- a/src/Avalonia.Themes.Default/SimpleTheme.cs +++ b/src/Avalonia.Themes.Default/SimpleTheme.cs @@ -44,7 +44,7 @@ namespace Avalonia.Themes.Default InitStyles(_baseUri); } - public event EventHandler OwnerChanged + public event EventHandler? OwnerChanged { add { diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml index 5b86de02d5..c5bb70bed3 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml @@ -587,9 +587,24 @@ - + + + + + + + + + + + + + + + + 1 diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml index eb68270354..8d38d39bd5 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml @@ -581,9 +581,24 @@ - + + + + + + + + + + + + + + + + 1 diff --git a/src/Avalonia.Themes.Fluent/Controls/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml index f545206a2f..3fb0d43342 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Button.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Button.xaml @@ -7,9 +7,11 @@ + 8,5,8,6 + - - + - + - + - + - + - + + + - diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index ffd3972b66..c6f70d05e1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -1,10 +1,3 @@ - - - 12 + 12 + 32 + + + - + + - - - - - - + + - - - - - + - - + - - - - - + -