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..5436ad22f3 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]) { @@ -709,4 +709,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..ec88852feb 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -20,10 +20,13 @@ - True + False + False True True - True + no-write-symbols,nodebug + Hybrid + True 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.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 f98956faac..d6a297858d 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/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/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/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..78001143d7 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 { /// 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 1b9a056272..80aebc02bc 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 { @@ -55,6 +56,7 @@ namespace Avalonia.Input PenBarrelButton = 2048 } + [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 98f7c81f02..52605bb6ae 100644 --- a/src/Avalonia.Base/Input/IPointer.cs +++ b/src/Avalonia.Base/Input/IPointer.cs @@ -1,3 +1,5 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// @@ -6,6 +8,7 @@ namespace Avalonia.Input /// /// Some devices, for instance, touchscreen might generate a pointer on each physical contact. /// + [NotClientImplementable] public interface IPointer { /// 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..f4e25ebada 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,7 +124,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent TextInputMethodClientRequestedEvent = RoutedEvent.Register( - "TextInputMethodClientRequested", + nameof(TextInputMethodClientRequested), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -144,7 +144,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent PointerMovedEvent = RoutedEvent.Register( - "PointerMove", + nameof(PointerMoved), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -152,7 +152,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent PointerPressedEvent = RoutedEvent.Register( - "PointerPressed", + nameof(PointerPressed), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -160,7 +160,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent PointerReleasedEvent = RoutedEvent.Register( - "PointerReleased", + nameof(PointerReleased), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// @@ -168,7 +168,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent PointerCaptureLostEvent = RoutedEvent.Register( - "PointerCaptureLost", + nameof(PointerCaptureLost), RoutingStrategies.Direct); /// @@ -176,7 +176,7 @@ namespace Avalonia.Input /// public static readonly RoutedEvent PointerWheelChangedEvent = RoutedEvent.Register( - "PointerWheelChanged", + nameof(PointerWheelChanged), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// 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/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 df7aa937a0..f30925f489 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, @@ -791,6 +790,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/GeometryDrawing.cs b/src/Avalonia.Base/Media/GeometryDrawing.cs index e46e3a7d5f..08e62df2cc 100644 --- a/src/Avalonia.Base/Media/GeometryDrawing.cs +++ b/src/Avalonia.Base/Media/GeometryDrawing.cs @@ -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/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index 22be8d8865..25c35a28e5 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -194,6 +194,25 @@ 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, out var scale); + + var geometry = new PlatformGeometry(geometryImpl); + + var transform = new MatrixTransform(Matrix.CreateTranslation(geometry.Bounds.Left, -geometry.Bounds.Top) * scale); + + geometry.Transform = transform; + + return geometry; + } + /// /// Retrieves the offset from the leading edge of the /// to the leading or trailing edge of a caret stop containing the specified character hit. 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/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/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/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs index 5d5d45db2d..4f7c43a6d1 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) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 73ec055bbe..8b5e2cc2ce 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -184,6 +184,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 +219,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 +248,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; @@ -254,28 +262,27 @@ namespace Avalonia.Media.TextFormatting { if (_flowDirection == 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 +293,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 +340,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 +411,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; + + 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); - skip: + 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 startIndex = currentRun.Text.Start + offset; + + 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)); - var textBounds = new TextBounds(currentRect, currentDirection); + startX += startOffset - currentShapedRun.Size.Width; - result[result.Count - 1] = textBounds; + 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; + + characterLength = currentRun.TextSourceLength; + } + } + + if (endX < startX) + { + (endX, startX) = (startX, endX); + } - currentRect = new Rect(startX, 0, width, Height); + //Lines that only contain a linebreak need to be covered here + if (characterLength == 0) + { + characterLength = NewLineLength; + } - result.Add(new TextBounds(currentRect, currentDirection)); + 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(); 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/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..bfa9e70fce 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,14 @@ 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 scaling of the produces geometry. + /// The geometry returned contains the combined geometry of all glyphs in the glyph run. + IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun, out Matrix scale); + /// /// 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 67e7d71fbc..fbdf0db800 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. @@ -242,18 +238,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/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..c4e8f47e67 100644 --- a/src/Avalonia.Base/Styling/PropertySetterInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterInstance.cs @@ -44,7 +44,7 @@ namespace Avalonia.Styling { if (hasActivator) { - if (_styledProperty is object) + if (_styledProperty is not null) { _subscription = _target.Bind(_styledProperty, this, BindingPriority.StyleTrigger); } @@ -55,13 +55,15 @@ 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); } } } 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..7c0bc4ad7f 100644 --- a/src/Avalonia.Base/Styling/Styles.cs +++ b/src/Avalonia.Base/Styling/Styles.cs @@ -20,7 +20,7 @@ namespace Avalonia.Styling private readonly AvaloniaList _styles = new AvaloniaList(); private IResourceHost? _owner; private IResourceDictionary? _resources; - private Dictionary?>? _cache; + private StyleCache? _cache; public Styles() { @@ -111,43 +111,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); } /// 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/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.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/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/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/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..bcac6324ba 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 cellMouseEnter = Cell_MouseEnter; + 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.PointerEnter += cellMouseEnter; + 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 monthMouseEnter = Month_MouseEnter; - 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.PointerEnter += monthMouseEnter; children.Add(month); } } 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/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/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..784d33ed58 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. @@ -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..58229a1772 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -85,13 +85,13 @@ namespace Avalonia.Controls /// Defines the event. /// public static readonly RoutedEvent PointerEnterItemEvent = - RoutedEvent.Register(nameof(PointerEnterItem), RoutingStrategies.Bubble); + RoutedEvent.Register(nameof(PointerEnterItem), RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent PointerLeaveItemEvent = - RoutedEvent.Register(nameof(PointerLeaveItem), RoutingStrategies.Bubble); + RoutedEvent.Register(nameof(PointerLeaveItem), RoutingStrategies.Bubble); /// /// Defines the event. 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..3a6d06f150 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; @@ -54,6 +56,7 @@ namespace Avalonia.Controls.Platform Menu.AddHandler(Avalonia.Controls.Menu.MenuOpenedEvent, this.MenuOpened); Menu.AddHandler(MenuItem.PointerEnterItemEvent, PointerEnter); Menu.AddHandler(MenuItem.PointerLeaveItemEvent, PointerLeave); + Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved); _root = Menu.VisualRoot; @@ -89,6 +92,7 @@ namespace Avalonia.Controls.Platform Menu.RemoveHandler(Avalonia.Controls.Menu.MenuOpenedEvent, this.MenuOpened); Menu.RemoveHandler(MenuItem.PointerEnterItemEvent, PointerEnter); Menu.RemoveHandler(MenuItem.PointerLeaveItemEvent, PointerLeave); + Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved); if (_root is InputElement inputRoot) { @@ -338,6 +342,22 @@ namespace Avalonia.Controls.Platform } } + 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 PointerLeave(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/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/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index a730659330..ea20247b4b 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -111,7 +111,7 @@ namespace Avalonia.Controls.Primitives /// public static readonly RoutedEvent SelectionChangedEvent = RoutedEvent.Register( - "SelectionChanged", + nameof(SelectionChanged), RoutingStrategies.Bubble); /// 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..1a69d1218c 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -631,7 +631,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; diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 45e66828c1..7652b23162 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -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 { @@ -239,23 +245,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,8 +273,8 @@ namespace Avalonia.Controls public bool IsReadOnly { - get { return GetValue(IsReadOnlyProperty); } - set { SetValue(IsReadOnlyProperty, value); } + get => GetValue(IsReadOnlyProperty); + set => SetValue(IsReadOnlyProperty, value); } public char PasswordChar @@ -301,11 +303,7 @@ namespace Avalonia.Controls public int SelectionStart { - get - { - return _selectionStart; - } - + get => _selectionStart; set { value = CoerceCaretIndex(value); @@ -325,11 +323,7 @@ namespace Avalonia.Controls public int SelectionEnd { - get - { - return _selectionEnd; - } - + get => _selectionEnd; set { value = CoerceCaretIndex(value); @@ -349,20 +343,29 @@ 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) @@ -386,7 +389,7 @@ namespace Avalonia.Controls public string SelectedText { - get { return GetSelection(); } + get => GetSelection(); set { if (string.IsNullOrEmpty(value)) @@ -407,8 +410,8 @@ namespace Avalonia.Controls /// public HorizontalAlignment HorizontalContentAlignment { - get { return GetValue(HorizontalContentAlignmentProperty); } - set { SetValue(HorizontalContentAlignmentProperty, value); } + get => GetValue(HorizontalContentAlignmentProperty); + set => SetValue(HorizontalContentAlignmentProperty, value); } /// @@ -416,50 +419,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 +478,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 +495,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 +504,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 +513,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 +522,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) @@ -1539,7 +1550,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/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.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.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..6471b87bfd 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -114,6 +114,13 @@ namespace Avalonia.Headless return new HeadlessGlyphRunStub(); } + public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun, out Matrix scale) + { + scale = Matrix.Identity; + + 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 + + + - + + - - - - - - + + - - - - - + - - + - - - - - + -