diff --git a/Avalonia.sln b/Avalonia.sln index a792774d94..1e2a3c6027 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -5,48 +5,26 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Visuals", "src\Avalonia.Visuals\Avalonia.Visuals.csproj", "{EB582467-6ABB-43A1-B052-E981BA910E3A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Layout", "src\Avalonia.Layout\Avalonia.Layout.csproj", "{42472427-4774-4C81-8AFF-9F27B8E31721}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Windows", "Windows", "{B39A8919-9F95-48FE-AD7B-76E08B509888}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32", "src\Windows\Avalonia.Win32\Avalonia.Win32.csproj", "{811A76CF-1CF6-440F-963B-BBE31BD72A82}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1", "src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj", "{3E908F67-5543-4879-A1DC-08EACE79B3CD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Input", "src\Avalonia.Input\Avalonia.Input.csproj", "{62024B2D-53EB-4638-B26B-85EEAA54866E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Interactivity", "src\Avalonia.Interactivity\Avalonia.Interactivity.csproj", "{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls", "src\Avalonia.Controls\Avalonia.Controls.csproj", "{D2221C82-4A25-4583-9B43-D791E3F6820C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Styling", "src\Avalonia.Styling\Avalonia.Styling.csproj", "{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Default", "src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Diagnostics", "src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj", "{7062AE20-5DCC-4442-9645-8195BDECE63E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Animation", "src\Avalonia.Animation\Avalonia.Animation.csproj", "{D211E587-D8BC-45B9-95A4-F297C8FA5200}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Styling.UnitTests", "tests\Avalonia.Styling.UnitTests\Avalonia.Styling.UnitTests.csproj", "{47ECDF59-DEF8-4C53-87B1-2098A3429059}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.UnitTests", "tests\Avalonia.Controls.UnitTests\Avalonia.Controls.UnitTests.csproj", "{5CCB5571-7C30-4E7D-967D-0E2158EBD91F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Visuals.UnitTests", "tests\Avalonia.Visuals.UnitTests\Avalonia.Visuals.UnitTests.csproj", "{76716382-3159-460E-BDA6-C5715CF606D7}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base.UnitTests", "tests\Avalonia.Base.UnitTests\Avalonia.Base.UnitTests.csproj", "{2905FF23-53FB-45E6-AA49-6AF47A172056}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Layout.UnitTests", "tests\Avalonia.Layout.UnitTests\Avalonia.Layout.UnitTests.csproj", "{DB070A10-BF39-4752-8456-86E9D5928478}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Interactivity.UnitTests", "tests\Avalonia.Interactivity.UnitTests\Avalonia.Interactivity.UnitTests.csproj", "{08478EF5-44E8-42E9-92D6-15E00EC038D8}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.RenderTests", "tests\Avalonia.Direct2D1.RenderTests\Avalonia.Direct2D1.RenderTests.csproj", "{DABFD304-D6A4-4752-8123-C2CCF7AC7831}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Input.UnitTests", "tests\Avalonia.Input.UnitTests\Avalonia.Input.UnitTests.csproj", "{AC18926A-E784-40FE-B09D-BB0FE2B599F0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.UnitTests", "tests\Avalonia.Direct2D1.UnitTests\Avalonia.Direct2D1.UnitTests.csproj", "{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.UnitTests", "tests\Avalonia.Markup.Xaml.UnitTests\Avalonia.Markup.Xaml.UnitTests.csproj", "{99135EAB-653D-47E4-A378-C96E1278CA44}" @@ -61,6 +39,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DE ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs + src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}" @@ -77,8 +56,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iOS", "iOS", "{0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.iOS", "src\iOS\Avalonia.iOS\Avalonia.iOS.csproj", "{4488AD85-1495-4809-9AA4-DDFE0A48527E}" @@ -117,6 +94,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\Base.props = build\Base.props build\Binding.props = build\Binding.props build\CoreLibraries.props = build\CoreLibraries.props + build\DevAnalyzers.props = build\DevAnalyzers.props build\EmbedXaml.props = build\EmbedXaml.props build\HarfBuzzSharp.props = build\HarfBuzzSharp.props build\JetBrains.Annotations.props = build\JetBrains.Annotations.props @@ -134,6 +112,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\SharedVersion.props = build\SharedVersion.props build\SharpDX.props = build\SharpDX.props build\SkiaSharp.props = build\SkiaSharp.props + build\SourceGenerators.props = build\SourceGenerators.props build\SourceLink.props = build\SourceLink.props build\System.Drawing.Common.props = build\System.Drawing.Common.props build\System.Memory.props = build\System.Memory.props @@ -184,8 +163,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Build.Tasks", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "nukebuild\_build.csproj", "{3F00BC43-5095-477F-93D8-E65B08179A00}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Animation.UnitTests", "tests\Avalonia.Animation.UnitTests\Avalonia.Animation.UnitTests.csproj", "{AF227847-E65C-4BE9-BCE9-B551357788E0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.X11", "src\Avalonia.X11\Avalonia.X11.csproj", "{41B02319-965D-4945-8005-C1A3D1224165}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformSanityChecks", "samples\PlatformSanityChecks\PlatformSanityChecks.csproj", "{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}" @@ -232,9 +209,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", "src\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj", "{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" +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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -276,54 +257,6 @@ Global {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 - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|Any CPU.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhone.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhone.Build.0 = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|Any CPU.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhone.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhone.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|Any CPU.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhone.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|Any CPU.Build.0 = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhone.Build.0 = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|Any CPU.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|Any CPU.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhone.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhone.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.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 @@ -372,54 +305,6 @@ Global {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 - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|Any CPU.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhone.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhone.Build.0 = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|Any CPU.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhone.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhone.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|Any CPU.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhone.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhone.Build.0 = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|Any CPU.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhone.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhone.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.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 @@ -444,30 +329,6 @@ Global {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 - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|Any CPU.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhone.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhone.Build.0 = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|Any CPU.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhone.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhone.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.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 @@ -516,54 +377,6 @@ Global {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 - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|Any CPU.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhone.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhone.Build.0 = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|Any CPU.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhone.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhone.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|Any CPU.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhone.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|Any CPU.Build.0 = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhone.Build.0 = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|Any CPU.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|Any CPU.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhone.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhone.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.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 @@ -588,30 +401,6 @@ Global {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 - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|Any CPU.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhone.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhone.Build.0 = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|Any CPU.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhone.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhone.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.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 @@ -636,54 +425,6 @@ Global {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 - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|Any CPU.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhone.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhone.Build.0 = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|Any CPU.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhone.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhone.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|Any CPU.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhone.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhone.Build.0 = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|Any CPU.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhone.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhone.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.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 @@ -708,30 +449,6 @@ Global {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 - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|Any CPU.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhone.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhone.Build.0 = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|Any CPU.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhone.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhone.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.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 @@ -914,22 +631,6 @@ Global {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 - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.Build.0 = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.Deploy.0 = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|iPhone.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.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 @@ -1638,30 +1339,6 @@ Global {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 - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhone.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhone.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|Any CPU.Build.0 = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.ActiveCfg = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.Build.0 = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.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 @@ -2238,6 +1915,54 @@ Global {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2245,14 +1970,9 @@ Global GlobalSection(NestedProjects) = preSolution {811A76CF-1CF6-440F-963B-BBE31BD72A82} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {3E908F67-5543-4879-A1DC-08EACE79B3CD} = {B39A8919-9F95-48FE-AD7B-76E08B509888} - {47ECDF59-DEF8-4C53-87B1-2098A3429059} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {5CCB5571-7C30-4E7D-967D-0E2158EBD91F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {76716382-3159-460E-BDA6-C5715CF606D7} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2905FF23-53FB-45E6-AA49-6AF47A172056} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {DB070A10-BF39-4752-8456-86E9D5928478} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {08478EF5-44E8-42E9-92D6-15E00EC038D8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {DABFD304-D6A4-4752-8123-C2CCF7AC7831} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {AC18926A-E784-40FE-B09D-BB0FE2B599F0} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {99135EAB-653D-47E4-A378-C96E1278CA44} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {3E53A01A-B331-47F3-B828-4A5717E77A24} = {8B6A8209-894F-4BA1-B880-965FD453982C} @@ -2260,7 +1980,6 @@ Global {8EF392D5-1416-45AA-9956-7CBBC3229E8A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162} = {9B9E3891-2366-4253-A952-D08BCEB71098} {7B92AF71-6287-4693-9DCB-BD5B6E927E23} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F} - {FF69B927-C545-49AE-8E16-3D14D621AA12} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F} {4488AD85-1495-4809-9AA4-DDFE0A48527E} = {0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1} {E1AA3DBF-9056-4530-9376-18119A7A3FFE} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {88060192-33D5-4932-B0F9-8BD2763E857D} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} @@ -2286,7 +2005,6 @@ Global {E1240B49-7B4B-4371-A00E-068778C5CF0B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {D49233F8-F29C-47DD-9975-C4C9E4502720} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C} {3C471044-3640-45E3-B1B2-16D2FF8399EE} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C} - {AF227847-E65C-4BE9-BCE9-B551357788E0} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {41B02319-965D-4945-8005-C1A3D1224165} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} @@ -2303,6 +2021,7 @@ Global {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/Directory.Build.props b/Directory.Build.props index 835decc672..97781b7517 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,5 +5,6 @@ false false + False diff --git a/NuGet.Config b/NuGet.Config index 7a1f28bea7..7d2bd8abd2 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -5,5 +5,6 @@ + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 79456b117b..edf3c3d819 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -31,14 +31,14 @@ jobs: vmImage: 'ubuntu-20.04' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.1.414' + displayName: 'Use .NET Core SDK 3.1.418' inputs: - version: 3.1.414 + version: 3.1.418 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.100' + displayName: 'Use .NET Core SDK 6.0.202' inputs: - version: 6.0.200 + version: 6.0.202 - task: CmdLine@2 displayName: 'Run Build' @@ -62,14 +62,14 @@ jobs: vmImage: 'macOS-10.15' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.1.414' + displayName: 'Use .NET Core SDK 3.1.418' inputs: - version: 3.1.414 + version: 3.1.418 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.100' + displayName: 'Use .NET Core SDK 6.0.202' inputs: - version: 6.0.200 + version: 6.0.202 - task: CmdLine@2 displayName: 'Install Mono 5.18' @@ -134,20 +134,20 @@ jobs: SolutionDir: '$(Build.SourcesDirectory)' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.1.414' + displayName: 'Use .NET Core SDK 3.1.418' inputs: - version: 3.1.414 + version: 3.1.418 - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 6.0.100' + displayName: 'Use .NET Core SDK 6.0.202' inputs: - version: 6.0.200 + version: 6.0.202 - task: CmdLine@2 displayName: 'Install Workloads' inputs: script: | - dotnet workload install --no-cache --disable-parallel android ios --skip-manifest-update --source "https://api.nuget.org/v3/index.json" + dotnet workload install android ios - task: CmdLine@2 displayName: 'Install Nuke' diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props index 6bf69603c0..314d38190a 100644 --- a/build/CoreLibraries.props +++ b/build/CoreLibraries.props @@ -1,14 +1,8 @@ - - - - - - diff --git a/build/DevAnalyzers.props b/build/DevAnalyzers.props new file mode 100644 index 0000000000..14e4f6a563 --- /dev/null +++ b/build/DevAnalyzers.props @@ -0,0 +1,9 @@ + + + + + diff --git a/build/HarfBuzzSharp.props b/build/HarfBuzzSharp.props index 6dd6cccb53..e10de93530 100644 --- a/build/HarfBuzzSharp.props +++ b/build/HarfBuzzSharp.props @@ -1,7 +1,7 @@  - - - + + + diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index 60bebaad40..a217a8272d 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,7 +1,7 @@  - - - + + + diff --git a/build/SourceGenerators.props b/build/SourceGenerators.props new file mode 100644 index 0000000000..d000af1bf6 --- /dev/null +++ b/build/SourceGenerators.props @@ -0,0 +1,10 @@ + + + + + + diff --git a/global.json b/global.json index 30265268dc..a6792b05c7 100644 --- a/global.json +++ b/global.json @@ -1,11 +1,10 @@ { "sdk": { - "version": "6.0.200", + "version": "6.0.202", "rollForward": "latestFeature" }, "msbuild-sdks": { "Microsoft.Build.Traversal": "1.0.43", - "Xamarin.Legacy.Sdk": "0.1.2-alpha6", "MSBuild.Sdk.Extras": "3.0.22", "AggregatePackage.NuGet.Sdk" : "0.1.12" } diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 79175d9ff1..05b129baca 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -73,6 +73,11 @@ ComPtr _events; _isHandlingSendEvent = true; @try { [super sendEvent: event]; + if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) + { + [[self keyWindow] sendEvent:event]; + } + } @finally { _isHandlingSendEvent = oldHandling; } diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 620b750a40..d16c466fe6 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -2461,6 +2461,16 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent if(_parent != nullptr) { + auto cparent = dynamic_cast(_parent.getRaw()); + + if(cparent != nullptr) + { + if(cparent->WindowState() == Maximized) + { + cparent->SetWindowState(Normal); + } + } + _parent->GetPosition(&position); _parent->BaseEvents->PositionChanged(position); } diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 72d90abbf3..f0f677b844 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -214,19 +214,14 @@ partial class Build : NukeBuild .DependsOn(Compile) .Executes(() => { - RunCoreTest("Avalonia.Animation.UnitTests"); RunCoreTest("Avalonia.Base.UnitTests"); RunCoreTest("Avalonia.Controls.UnitTests"); RunCoreTest("Avalonia.Controls.DataGrid.UnitTests"); - RunCoreTest("Avalonia.Input.UnitTests"); - RunCoreTest("Avalonia.Interactivity.UnitTests"); - RunCoreTest("Avalonia.Layout.UnitTests"); RunCoreTest("Avalonia.Markup.UnitTests"); RunCoreTest("Avalonia.Markup.Xaml.UnitTests"); - RunCoreTest("Avalonia.Styling.UnitTests"); - RunCoreTest("Avalonia.Visuals.UnitTests"); RunCoreTest("Avalonia.Skia.UnitTests"); RunCoreTest("Avalonia.ReactiveUI.UnitTests"); + RunCoreTest("Avalonia.PlatformSupport.UnitTests"); }); Target RunRenderTests => _ => _ diff --git a/readme.md b/readme.md index 7e32dbc321..1cdaf3b8f8 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@ [![Telegram](https://raw.githubusercontent.com/Patrolavia/telegram-badge/master/chat.svg)](https://t.me/Avalonia) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) [![Discord](https://img.shields.io/badge/discord-join%20chat-46BC99)]( https://aka.ms/dotnet-discord) [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) ![License](https://img.shields.io/github/license/avaloniaui/avalonia.svg)
-[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) +[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) ## 📖 About diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 9777bb46c3..04c67e84e8 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -39,9 +39,9 @@
- + -
+ \ No newline at end of file diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index d1b657722c..2b45ac1508 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -6,7 +6,7 @@ true - + true https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json 7.0.0-* @@ -22,12 +22,11 @@ - + - diff --git a/samples/ControlCatalog.NetCore/rd.xml b/samples/ControlCatalog.NetCore/rd.xml deleted file mode 100644 index 27db7f34ca..0000000000 --- a/samples/ControlCatalog.NetCore/rd.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj index 520bbdf32b..b2c9ec72eb 100644 --- a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj +++ b/samples/ControlCatalog.Web/ControlCatalog.Web.csproj @@ -1,15 +1,14 @@  net6.0 - false enable - True + + true + 16777216 + false + false - - - - false @@ -23,19 +22,36 @@ -O3 -O3 false + false + false + false + false + false + true + false + true + true + true + link + true - - + + - - - - - + + + + + + + + + + diff --git a/samples/ControlCatalog.Web/LinkerConfig.xml b/samples/ControlCatalog.Web/LinkerConfig.xml deleted file mode 100644 index 5839a0fe03..0000000000 --- a/samples/ControlCatalog.Web/LinkerConfig.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj index 12d1d5645e..513ac44f83 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj +++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj @@ -7,10 +7,10 @@ True iossimulator-x64 - + - + \ No newline at end of file diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index f3b52428ca..85f278b5fa 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -190,6 +190,14 @@ Mica + + + LeftToRight + RightToLeft + + diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index 79cf07c8d9..e8ea39abbb 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -76,6 +76,15 @@ namespace ControlCatalog } }; + var flowDirections = this.Find("FlowDirection"); + flowDirections.SelectionChanged += (sender, e) => + { + if (flowDirections.SelectedItem is FlowDirection flowDirection) + { + this.FlowDirection = flowDirection; + } + }; + var decorations = this.Find("Decorations"); decorations.SelectionChanged += (sender, e) => { diff --git a/samples/ControlCatalog/Pages/ButtonsPage.xaml b/samples/ControlCatalog/Pages/ButtonsPage.xaml index 7de0872eae..059b4d9788 100644 --- a/samples/ControlCatalog/Pages/ButtonsPage.xaml +++ b/samples/ControlCatalog/Pages/ButtonsPage.xaml @@ -147,6 +147,35 @@ + + + + + A button with an added drop-down chevron to visually indicate it has a flyout with additional actions. + + + + + + + + + + + + Disabled + + + + + Content="Re-themed" + Foreground="White"> + + + + @@ -31,7 +44,9 @@ - + diff --git a/samples/ControlCatalog/Pages/PointersPage.cs b/samples/ControlCatalog/Pages/PointersPage.cs index 2901013cea..0377993d2c 100644 --- a/samples/ControlCatalog/Pages/PointersPage.cs +++ b/samples/ControlCatalog/Pages/PointersPage.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Threading; using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Documents; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Media; @@ -131,7 +132,7 @@ public class PointersPage : Decorator { public PointerIntermediatePointsTab() { - this[TextBlock.ForegroundProperty] = Brushes.Black; + this[TextElement.ForegroundProperty] = Brushes.Black; var slider = new Slider { Margin = new Thickness(5), diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index caad8b0854..3d3ed00aac 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -13,6 +13,8 @@ namespace ControlCatalog.Pages { private double _leftMost; + protected override bool BypassFlowDirectionPolicies => true; + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); diff --git a/samples/IntegrationTestApp/MainWindow.axaml b/samples/IntegrationTestApp/MainWindow.axaml index fe1487a7f2..19ac68b15b 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml +++ b/samples/IntegrationTestApp/MainWindow.axaml @@ -82,12 +82,16 @@ - + - None + + None + + + diff --git a/samples/IntegrationTestApp/MainWindow.axaml.cs b/samples/IntegrationTestApp/MainWindow.axaml.cs index b9e631a312..9a612aa94d 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml.cs +++ b/samples/IntegrationTestApp/MainWindow.axaml.cs @@ -62,6 +62,8 @@ namespace IntegrationTestApp this.FindControl("BasicComboBox").SelectedIndex = 0; if (source?.Name == "ListBoxSelectionClear") this.FindControl("BasicListBox").SelectedIndex = -1; + if (source?.Name == "MenuClickedMenuItemReset") + this.FindControl("ClickedMenuItem").Text = "None"; } } } diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index 923b51814f..429c4776d5 100644 --- a/samples/RenderDemo/MainWindow.xaml +++ b/samples/RenderDemo/MainWindow.xaml @@ -60,6 +60,9 @@ + + + diff --git a/samples/RenderDemo/Pages/PathMeasurementPage.cs b/samples/RenderDemo/Pages/PathMeasurementPage.cs index 212377deae..cc5125609c 100644 --- a/samples/RenderDemo/Pages/PathMeasurementPage.cs +++ b/samples/RenderDemo/Pages/PathMeasurementPage.cs @@ -1,15 +1,9 @@ -using System; -using System.Diagnostics; -using System.Drawing.Drawing2D; -using System.Security.Cryptography; using Avalonia; using Avalonia.Controls; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Media.Immutable; -using Avalonia.Threading; -using Avalonia.Visuals.Media.Imaging; namespace RenderDemo.Pages { diff --git a/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs b/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs index f263786ab7..f365b59c20 100644 --- a/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs +++ b/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs @@ -5,7 +5,6 @@ using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Threading; -using Avalonia.Visuals.Media.Imaging; namespace RenderDemo.Pages { diff --git a/samples/RenderDemo/Pages/TextFormatterPage.axaml b/samples/RenderDemo/Pages/TextFormatterPage.axaml new file mode 100644 index 0000000000..4edf0852a2 --- /dev/null +++ b/samples/RenderDemo/Pages/TextFormatterPage.axaml @@ -0,0 +1,7 @@ + + diff --git a/samples/RenderDemo/Pages/TextFormatterPage.axaml.cs b/samples/RenderDemo/Pages/TextFormatterPage.axaml.cs new file mode 100644 index 0000000000..92eb2e7dec --- /dev/null +++ b/samples/RenderDemo/Pages/TextFormatterPage.axaml.cs @@ -0,0 +1,118 @@ +using System; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Media; +using Avalonia.Media.TextFormatting; + +namespace RenderDemo.Pages +{ + public class TextFormatterPage : UserControl + { + private TextLine _textLine; + + public TextFormatterPage() + { + this.InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public override void Render(DrawingContext context) + { + _textLine?.Draw(context, new Point()); + } + + protected override Size MeasureOverride(Size availableSize) + { + var defaultRunProperties = new GenericTextRunProperties(Typeface.Default, foregroundBrush: Brushes.Black, + baselineAlignment: BaselineAlignment.Center); + var paragraphProperties = new GenericTextParagraphProperties(defaultRunProperties); + + var control = new Button { Content = new TextBlock { Text = "ClickMe" } }; + + Content = control; + + var textSource = new CustomTextSource(control, defaultRunProperties); + + control.Measure(Size.Infinity); + + _textLine = + TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties); + + return base.MeasureOverride(availableSize); + } + + protected override Size ArrangeOverride(Size finalSize) + { + var currentX = 0d; + + foreach (var textRun in _textLine.TextRuns) + { + if (textRun is ControlRun controlRun) + { + controlRun.Control.Arrange(new Rect(new Point(currentX, 0), controlRun.Size)); + } + + if (textRun is DrawableTextRun drawableTextRun) + { + currentX += drawableTextRun.Size.Width; + } + } + + return finalSize; + } + + private class CustomTextSource : ITextSource + { + private readonly Control _control; + private readonly TextRunProperties _defaultProperties; + private readonly string _text = "<-Hello World->"; + + public CustomTextSource(Control control, TextRunProperties defaultProperties) + { + _control = control; + _defaultProperties = defaultProperties; + } + + public TextRun? GetTextRun(int textSourceIndex) + { + if (textSourceIndex >= _text.Length * 2 + TextRun.DefaultTextSourceLength) + { + return null; + } + + if (textSourceIndex == _text.Length) + { + return new ControlRun(_control, _defaultProperties); + } + + return new TextCharacters(_text.AsMemory(), _defaultProperties); + } + } + + private class ControlRun : DrawableTextRun + { + private readonly Control _control; + + public ControlRun(Control control, TextRunProperties properties) + { + _control = control; + Properties = properties; + } + + public Control Control => _control; + public override Size Size => _control.DesiredSize; + public override double Baseline => 0; + public override TextRunProperties? Properties { get; } + + public override void Draw(DrawingContext drawingContext, Point origin) + { + // noop + } + } + } +} diff --git a/samples/RenderDemo/RenderDemo.csproj b/samples/RenderDemo/RenderDemo.csproj index 54d5ca4b3b..18a4ee5662 100644 --- a/samples/RenderDemo/RenderDemo.csproj +++ b/samples/RenderDemo/RenderDemo.csproj @@ -4,7 +4,11 @@ net6.0 - + + + TextFormatterPage.axaml + Code + diff --git a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml index e0dfa49a44..1d58c465a0 100644 --- a/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml +++ b/samples/SampleControls/HamburgerMenu/HamburgerMenu.xaml @@ -195,9 +195,9 @@ VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" - TextBlock.FontFamily="{TemplateBinding FontFamily}" - TextBlock.FontSize="{TemplateBinding FontSize}" - TextBlock.FontWeight="{TemplateBinding FontWeight}" /> + TextElement.FontFamily="{TemplateBinding FontFamily}" + TextElement.FontSize="{TemplateBinding FontSize}" + TextElement.FontWeight="{TemplateBinding FontWeight}" /> @@ -216,25 +216,25 @@ Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" CornerRadius="{TemplateBinding CornerRadius}" - TextBlock.FontFamily="{TemplateBinding FontFamily}" - TextBlock.FontSize="{TemplateBinding FontSize}" - TextBlock.FontWeight="{TemplateBinding FontWeight}" /> + TextElement.FontFamily="{TemplateBinding FontFamily}" + TextElement.FontSize="{TemplateBinding FontSize}" + TextElement.FontWeight="{TemplateBinding FontWeight}" /> + @@ -102,22 +116,35 @@ + + + + + @@ -180,57 +188,58 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - + Fill="{TemplateBinding SeparatorBrush}" + IsVisible="{TemplateBinding AreSeparatorsVisible}" /> + + + + + - + @@ -271,38 +280,51 @@ - - - - - - - - + + + + + + + - + DataGridFrozenGrid.IsFrozen="True" /> + + - + + + + + @@ -430,9 +452,12 @@ Width="12" Height="12" Margin="12,0,0,0" + BorderBrush="{TemplateBinding BorderBrush}" + BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" - Foreground="{TemplateBinding Foreground}" - Focusable="False" /> + CornerRadius="{TemplateBinding CornerRadius}" + Focusable="False" + Foreground="{TemplateBinding Foreground}" /> - - + CornerRadius="{TemplateBinding CornerRadius}"> + diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index ce84a7fe84..fe3ac31734 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -1,4 +1,6 @@ Compat issues with assembly Avalonia.Controls: +TypesMustExist : Type 'Avalonia.Controls.DropDown' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'Avalonia.Controls.DropDownItem' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Controls.IMenuItem.StaysOpenOnClick.get()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.IMenuItem.StaysOpenOnClick.set(System.Boolean)' is present in the implementation but not in the contract. @@ -29,6 +31,26 @@ MembersMustExist : Member 'public void Avalonia.Controls.NumericUpDownValueChang MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.NewValue.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Double Avalonia.Controls.NumericUpDownValueChangedEventArgs.OldValue.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.ScrollViewer.AllowAutoHideProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.FontFamilyProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.FontStyleProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.FontWeightProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.ForegroundProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.AttachedProperty Avalonia.AttachedProperty Avalonia.Controls.TextBlock.FontSizeProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.TextAlignmentProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.TextTrimmingProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.TextWrappingProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.LineHeightProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.TextBlock.MaxLinesProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Media.FontFamily Avalonia.Controls.TextBlock.GetFontFamily(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public System.Double Avalonia.Controls.TextBlock.GetFontSize(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Media.FontStyle Avalonia.Controls.TextBlock.GetFontStyle(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Media.FontWeight Avalonia.Controls.TextBlock.GetFontWeight(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Media.IBrush Avalonia.Controls.TextBlock.GetForeground(Avalonia.Controls.Control)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontFamily(Avalonia.Controls.Control, Avalonia.Media.FontFamily)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontSize(Avalonia.Controls.Control, System.Double)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontStyle(Avalonia.Controls.Control, Avalonia.Media.FontStyle)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetFontWeight(Avalonia.Controls.Control, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Controls.TextBlock.SetForeground(Avalonia.Controls.Control, Avalonia.Media.IBrush)' does not exist in the implementation but it does exist in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.TopLevel' does not implement interface 'Avalonia.Utilities.IWeakSubscriber' in the implementation but it does in the contract. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Viewbox' does not inherit from base type 'Avalonia.Controls.Decorator' in the implementation but it does in the contract. MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaProperty Avalonia.Controls.Viewbox.StretchProperty' does not exist in the implementation but it does exist in the contract. @@ -72,4 +94,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract. -Total Issues: 73 +Total Issues: 95 diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 76e2d3a161..c59458311c 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -65,7 +65,7 @@ namespace Avalonia.Controls.ApplicationLifetimes /// public Window? MainWindow { get; set; } - public IReadOnlyList Windows => _windows.ToList(); + public IReadOnlyList Windows => _windows.ToArray(); private void HandleWindowClosed(Window window) { diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 930e250334..3316c06bf5 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -252,6 +252,10 @@ namespace Avalonia.Controls /// drop-down that contains possible matches based on the input in the text /// box. /// + [TemplatePart(ElementPopup, typeof(Popup))] + [TemplatePart(ElementSelector, typeof(SelectingItemsControl))] + [TemplatePart(ElementSelectionAdapter, typeof(ISelectionAdapter))] + [TemplatePart(ElementTextBox, typeof(TextBox))] [PseudoClasses(":dropdownopen")] public class AutoCompleteBox : TemplatedControl { @@ -2180,7 +2184,7 @@ namespace Avalonia.Controls } // Store a local cached copy of the data - _items = newValue == null ? null : new List(newValue.Cast().ToList()); + _items = newValue == null ? null : new List(newValue.Cast()); // Clear and set the view on the selection adapter ClearView(); @@ -2239,7 +2243,7 @@ namespace Avalonia.Controls ClearView(); if (Items != null) { - _items = new List(Items.Cast().ToList()); + _items = new List(Items.Cast()); } } diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 543a513d57..4d239e69f4 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -6,17 +6,12 @@ - - - - - - + diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 899521536f..a4d15bab8d 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -29,11 +29,14 @@ namespace Avalonia.Controls } /// - /// A button control. + /// A standard button control. /// - [PseudoClasses(":pressed")] - public class Button : ContentControl, ICommandSource + [PseudoClasses(pcFlyoutOpen, pcPressed)] + public class Button : ContentControl, ICommandSource, IClickableControl { + protected const string pcPressed = ":pressed"; + protected const string pcFlyoutOpen = ":flyout-open"; + /// /// Defines the property. /// @@ -92,6 +95,7 @@ namespace Avalonia.Controls private ICommand? _command; private bool _commandCanExecute = true; private KeyGesture? _hotkey; + private bool _isFlyoutOpen = false; /// /// Initializes static members of the class. @@ -107,7 +111,6 @@ namespace Avalonia.Controls /// public Button() { - UpdatePseudoClasses(IsPressed); } /// @@ -328,11 +331,30 @@ namespace Avalonia.Controls } } + /// + /// Opens the button's flyout. + /// protected virtual void OpenFlyout() { Flyout?.ShowAt(this); } + /// + /// Invoked when the button's flyout is opened. + /// + protected virtual void OnFlyoutOpened() + { + // Available for derived types + } + + /// + /// Invoked when the button's flyout is closed. + /// + protected virtual void OnFlyoutClosed() + { + // Available for derived types + } + /// protected override void OnPointerPressed(PointerPressedEventArgs e) { @@ -382,6 +404,14 @@ namespace Avalonia.Controls IsPressed = false; } + /// + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + UnregisterFlyoutEvents(Flyout); + RegisterFlyoutEvents(Flyout); + UpdatePseudoClasses(); + } + /// protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { @@ -442,17 +472,26 @@ namespace Avalonia.Controls } else if (change.Property == IsPressedProperty) { - UpdatePseudoClasses(change.NewValue.GetValueOrDefault()); + UpdatePseudoClasses(); } else if (change.Property == FlyoutProperty) { + var oldFlyout = change.OldValue.GetValueOrDefault() as FlyoutBase; + var newFlyout = change.NewValue.GetValueOrDefault() as FlyoutBase; + // If flyout is changed while one is already open, make sure we // close the old one first - if (change.OldValue.GetValueOrDefault() is FlyoutBase oldFlyout && + if (oldFlyout != null && oldFlyout.IsOpen) { oldFlyout.Hide(); } + + // Must unregister events here while a reference to the old flyout still exists + UnregisterFlyoutEvents(oldFlyout); + + RegisterFlyoutEvents(newFlyout); + UpdatePseudoClasses(); } } @@ -493,6 +532,32 @@ namespace Avalonia.Controls } } + /// + /// Registers all flyout events. + /// + /// The flyout to connect events to. + private void RegisterFlyoutEvents(FlyoutBase? flyout) + { + if (flyout != null) + { + flyout.Opened += Flyout_Opened; + flyout.Closed += Flyout_Closed; + } + } + + /// + /// Explicitly unregisters all flyout events. + /// + /// The flyout to disconnect events from. + private void UnregisterFlyoutEvents(FlyoutBase? flyout) + { + if (flyout != null) + { + flyout.Opened -= Flyout_Opened; + flyout.Closed -= Flyout_Closed; + } + } + /// /// Starts listening for the Enter key when the button . /// @@ -560,11 +625,53 @@ namespace Avalonia.Controls /// /// Updates the visual state of the control by applying latest PseudoClasses. /// - private void UpdatePseudoClasses(bool isPressed) + private void UpdatePseudoClasses() { - PseudoClasses.Set(":pressed", isPressed); + PseudoClasses.Set(pcFlyoutOpen, _isFlyoutOpen); + PseudoClasses.Set(pcPressed, IsPressed); } void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e); + + void IClickableControl.RaiseClick() => OnClick(); + + /// + /// Event handler for when the button's flyout is opened. + /// + private void Flyout_Opened(object? sender, EventArgs e) + { + var flyout = sender as FlyoutBase; + + // It is possible to share flyouts among multiple controls including Button. + // This can cause a problem here since all controls that share a flyout receive + // the same Opened/Closed events at the same time. + // For Button that means they all would be updating their pseudoclasses accordingly. + // In other words, all Buttons with a shared Flyout would have the backgrounds changed together. + // To fix this, only continue here if the Flyout target matches this Button instance. + if (object.ReferenceEquals(flyout?.Target, this)) + { + _isFlyoutOpen = true; + UpdatePseudoClasses(); + + OnFlyoutOpened(); + } + } + + /// + /// Event handler for when the button's flyout is closed. + /// + private void Flyout_Closed(object? sender, EventArgs e) + { + var flyout = sender as FlyoutBase; + + // See comments in Flyout_Opened + if (object.ReferenceEquals(flyout?.Target, this)) + { + _isFlyoutOpen = false; + UpdatePseudoClasses(); + + OnFlyoutClosed(); + } + } } } diff --git a/src/Avalonia.Controls/ButtonSpinner.cs b/src/Avalonia.Controls/ButtonSpinner.cs index 31aba024ae..29a954098f 100644 --- a/src/Avalonia.Controls/ButtonSpinner.cs +++ b/src/Avalonia.Controls/ButtonSpinner.cs @@ -16,6 +16,8 @@ namespace Avalonia.Controls /// /// Represents a spinner control that includes two Buttons. /// + [TemplatePart("PART_DecreaseButton", typeof(Button))] + [TemplatePart("PART_IncreaseButton", typeof(Button))] [PseudoClasses(":left", ":right")] public class ButtonSpinner : Spinner { diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs index 6c83308b39..2dbb5f02f9 100644 --- a/src/Avalonia.Controls/Calendar/Calendar.cs +++ b/src/Avalonia.Controls/Calendar/Calendar.cs @@ -6,6 +6,7 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Input; @@ -222,6 +223,8 @@ namespace Avalonia.Controls /// element in XAML. /// /// + [TemplatePart(PART_ElementMonth, typeof(CalendarItem))] + [TemplatePart(PART_ElementRoot, typeof(Panel))] public class Calendar : TemplatedControl { internal const int RowsPerMonth = 7; @@ -261,6 +264,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register( nameof(FirstDayOfWeek), defaultValue: DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek); + /// /// Gets or sets the day that is considered the beginning of the week. /// @@ -273,6 +277,7 @@ namespace Avalonia.Controls get { return GetValue(FirstDayOfWeekProperty); } set { SetValue(FirstDayOfWeekProperty, value); } } + /// /// FirstDayOfWeekProperty property changed handler. /// @@ -289,6 +294,7 @@ namespace Avalonia.Controls throw new ArgumentOutOfRangeException("d", "Invalid DayOfWeek"); } } + /// /// Inherited code: Requires comment. /// @@ -311,6 +317,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register( nameof(IsTodayHighlighted), defaultValue: true); + /// /// Gets or sets a value indicating whether the current date is /// highlighted. @@ -324,6 +331,7 @@ namespace Avalonia.Controls get { return GetValue(IsTodayHighlightedProperty); } set { SetValue(IsTodayHighlightedProperty, value); } } + /// /// IsTodayHighlightedProperty property changed handler. /// @@ -343,6 +351,7 @@ namespace Avalonia.Controls public static readonly StyledProperty HeaderBackgroundProperty = AvaloniaProperty.Register(nameof(HeaderBackground)); + public IBrush HeaderBackground { get { return GetValue(HeaderBackgroundProperty); } @@ -367,6 +376,7 @@ namespace Avalonia.Controls get { return GetValue(DisplayModeProperty); } set { SetValue(DisplayModeProperty, value); } } + /// /// DisplayModeProperty property changed handler. /// @@ -424,6 +434,7 @@ namespace Avalonia.Controls || mode == CalendarMode.Year || mode == CalendarMode.Decade; } + private void OnDisplayModeChanged(CalendarModeChangedEventArgs args) { DisplayModeChanged?.Invoke(this, args); @@ -433,6 +444,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register( nameof(SelectionMode), defaultValue: CalendarSelectionMode.SingleDate); + /// /// Gets or sets a value that indicates what kind of selections are /// allowed. @@ -457,6 +469,7 @@ namespace Avalonia.Controls get { return GetValue(SelectionModeProperty); } set { SetValue(SelectionModeProperty, value); } } + private void OnSelectionModeChanged(AvaloniaPropertyChangedEventArgs e) { if (IsValidSelectionMode(e.NewValue!)) @@ -471,6 +484,7 @@ namespace Avalonia.Controls throw new ArgumentOutOfRangeException("d", "Invalid SelectionMode"); } } + /// /// Inherited code: Requires comment. /// @@ -492,6 +506,7 @@ namespace Avalonia.Controls o => o.SelectedDate, (o, v) => o.SelectedDate = v, defaultBindingMode: BindingMode.TwoWay); + /// /// Gets or sets the currently selected date. /// @@ -720,6 +735,7 @@ namespace Avalonia.Controls o => o.DisplayDate, (o, v) => o.DisplayDate = v, defaultBindingMode: BindingMode.TwoWay); + /// /// Gets or sets the date to display. /// @@ -1973,6 +1989,7 @@ namespace Avalonia.Controls } } } + private void Calendar_KeyUp(KeyEventArgs e) { if (!e.Handled && (e.Key == Key.LeftShift || e.Key == Key.RightShift)) @@ -1980,6 +1997,7 @@ namespace Avalonia.Controls ProcessShiftKeyUp(); } } + internal void ProcessShiftKeyUp() { if (_isShiftPressed && (SelectionMode == CalendarSelectionMode.SingleRange || SelectionMode == CalendarSelectionMode.MultipleRange)) @@ -2028,6 +2046,7 @@ namespace Avalonia.Controls } } } + protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); @@ -2054,6 +2073,7 @@ namespace Avalonia.Controls } } } + /// /// Called when the IsEnabled property changes. /// @@ -2098,6 +2118,7 @@ namespace Avalonia.Controls private const string PART_ElementRoot = "Root"; private const string PART_ElementMonth = "CalendarItem"; + /// /// Builds the visual tree for the /// when a new diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index f1c56a7331..0ac2056ed1 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -7,6 +7,7 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Input; @@ -116,6 +117,10 @@ namespace Avalonia.Controls Custom = 2 } + [TemplatePart(ElementButton, typeof(Button))] + [TemplatePart(ElementCalendar, typeof(Calendar))] + [TemplatePart(ElementPopup, typeof(Popup))] + [TemplatePart(ElementTextBox, typeof(TextBox))] public class CalendarDatePicker : TemplatedControl { private const string ElementTextBox = "PART_TextBox"; diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs index 616b9083ff..c44994f92f 100644 --- a/src/Avalonia.Controls/Calendar/CalendarItem.cs +++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs @@ -19,6 +19,11 @@ namespace Avalonia.Controls.Primitives /// Represents the currently displayed month or year on a /// . /// + [TemplatePart(PART_ElementHeaderButton, typeof(Button))] + [TemplatePart(PART_ElementMonthView, typeof(Grid))] + [TemplatePart(PART_ElementNextButton, typeof(Button))] + [TemplatePart(PART_ElementPreviousButton, typeof(Button))] + [TemplatePart(PART_ElementYearView, typeof(Grid))] [PseudoClasses(":calendardisabled")] public sealed class CalendarItem : TemplatedControl { diff --git a/src/Avalonia.Controls/Calendar/DateTimeHelper.cs b/src/Avalonia.Controls/Calendar/DateTimeHelper.cs index eb90f6c399..7a5c74a51b 100644 --- a/src/Avalonia.Controls/Calendar/DateTimeHelper.cs +++ b/src/Avalonia.Controls/Calendar/DateTimeHelper.cs @@ -68,10 +68,7 @@ namespace Avalonia.Controls public 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; + return new DateTime(d.Year, d.Month, 1, 0, 0, 0); } [return: NotNullIfNotNull("d")] diff --git a/src/Avalonia.Controls/Chrome/CaptionButtons.cs b/src/Avalonia.Controls/Chrome/CaptionButtons.cs index 1cad1a4c69..d5923a8b37 100644 --- a/src/Avalonia.Controls/Chrome/CaptionButtons.cs +++ b/src/Avalonia.Controls/Chrome/CaptionButtons.cs @@ -8,6 +8,10 @@ namespace Avalonia.Controls.Chrome /// /// Draws window minimize / maximize / close buttons in a when managed client decorations are enabled. /// + [TemplatePart("PART_CloseButton", typeof(Panel))] + [TemplatePart("PART_RestoreButton", typeof(Panel))] + [TemplatePart("PART_MinimiseButton", typeof(Panel))] + [TemplatePart("PART_FullScreenButton", typeof(Panel))] [PseudoClasses(":minimized", ":normal", ":maximized", ":fullscreen")] public class CaptionButtons : TemplatedControl { diff --git a/src/Avalonia.Controls/Chrome/TitleBar.cs b/src/Avalonia.Controls/Chrome/TitleBar.cs index 4da50e7220..b152a31587 100644 --- a/src/Avalonia.Controls/Chrome/TitleBar.cs +++ b/src/Avalonia.Controls/Chrome/TitleBar.cs @@ -8,6 +8,7 @@ namespace Avalonia.Controls.Chrome /// /// Draws a titlebar when managed client decorations are enabled. /// + [TemplatePart("PART_CaptionButtons", typeof(CaptionButtons))] [PseudoClasses(":minimized", ":normal", ":maximized", ":fullscreen")] public class TitleBar : TemplatedControl { diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index c5410ae9b0..cbf9b35a05 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -13,12 +13,14 @@ using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Media; using Avalonia.VisualTree; +using Avalonia.Controls.Metadata; namespace Avalonia.Controls { /// /// A drop-down list control. /// + [TemplatePart("PART_Popup", typeof(Popup))] public class ComboBox : SelectingItemsControl { /// @@ -182,6 +184,25 @@ namespace Avalonia.Controls this.UpdateSelectionBoxItem(SelectedItem); } + // Because the SelectedItem isn't connected to the visual tree + public override void InvalidateMirrorTransform() + { + base.InvalidateMirrorTransform(); + + if (SelectedItem is Control selectedControl) + { + selectedControl.InvalidateMirrorTransform(); + + foreach (var visual in selectedControl.GetVisualDescendants()) + { + if (visual is Control childControl) + { + childControl.InvalidateMirrorTransform(); + } + } + } + } + /// protected override void OnKeyDown(KeyEventArgs e) { @@ -432,42 +453,18 @@ namespace Avalonia.Controls private void SelectNext() { - int next = SelectedIndex + 1; - - if (next >= ItemCount) + if (ItemCount >= 1) { - if (WrapSelection == true) - { - next = 0; - } - else - { - return; - } + MoveSelection(NavigationDirection.Next, WrapSelection); } - - - - SelectedIndex = next; } private void SelectPrev() { - int prev = SelectedIndex - 1; - - if (prev < 0) + if (ItemCount >= 1) { - if (WrapSelection == true) - { - prev = ItemCount - 1; - } - else - { - return; - } + MoveSelection(NavigationDirection.Previous, WrapSelection); } - - SelectedIndex = prev; } } } diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs index ac7d24be92..b8a45e102f 100644 --- a/src/Avalonia.Controls/ContentControl.cs +++ b/src/Avalonia.Controls/ContentControl.cs @@ -1,4 +1,5 @@ using Avalonia.Collections; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Mixins; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; @@ -12,6 +13,7 @@ namespace Avalonia.Controls /// /// Displays according to a . /// + [TemplatePart("PART_ContentPresenter", typeof(IContentPresenter))] public class ContentControl : TemplatedControl, IContentControl, IContentPresenterHost { /// diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index cec662aad8..2c696c8f74 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -1,7 +1,7 @@ using System; using System.ComponentModel; -using System.Runtime.CompilerServices; using Avalonia.Automation.Peers; +using Avalonia.Controls.Documents; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Input; @@ -67,7 +67,7 @@ namespace Avalonia.Controls /// public static readonly AttachedProperty FlowDirectionProperty = AvaloniaProperty.RegisterAttached(nameof(FlowDirection), inherits: true); - + private DataTemplates? _dataTemplates; private IControl? _focusAdorner; private AutomationPeer? _automationPeer; @@ -137,9 +137,39 @@ namespace Avalonia.Controls public new IControl? Parent => (IControl?)base.Parent; + /// + /// Gets the value of the attached on a control. + /// + /// The control. + /// The flow direction. + public static FlowDirection GetFlowDirection(Control control) + { + return control.GetValue(FlowDirectionProperty); + } + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetFlowDirection(Control control, FlowDirection value) + { + control.SetValue(FlowDirectionProperty, value); + } + /// bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null; + /// + /// Gets a value indicating whether control bypass FlowDirecton policies. + /// + /// + /// Related to FlowDirection system and returns false as default, so if + /// is RTL then control will get a mirror presentation. + /// For controls that want to avoid this behavior, override this property and return true. + /// + protected virtual bool BypassFlowDirectionPolicies => false; + /// void ISetterValue.Initialize(ISetter setter) { @@ -199,6 +229,14 @@ namespace Avalonia.Controls base.OnDetachedFromVisualTreeCore(e); } + /// + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + InvalidateMirrorTransform(); + } + /// protected override void OnGotFocus(GotFocusEventArgs e) { @@ -309,5 +347,55 @@ namespace Avalonia.Controls } } } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == FlowDirectionProperty) + { + InvalidateMirrorTransform(); + + foreach (var visual in VisualChildren) + { + if (visual is Control child) + { + child.InvalidateMirrorTransform(); + } + } + } + } + + /// + /// Computes the value according to the + /// and + /// + public virtual void InvalidateMirrorTransform() + { + var flowDirection = this.FlowDirection; + var parentFlowDirection = FlowDirection.LeftToRight; + + bool bypassFlowDirectionPolicies = BypassFlowDirectionPolicies; + bool parentBypassFlowDirectionPolicies = false; + + var parent = this.FindAncestorOfType(); + if (parent != null) + { + parentFlowDirection = parent.FlowDirection; + parentBypassFlowDirectionPolicies = parent.BypassFlowDirectionPolicies; + } + else if (Parent is Control logicalParent) + { + parentFlowDirection = logicalParent.FlowDirection; + parentBypassFlowDirectionPolicies = logicalParent.BypassFlowDirectionPolicies; + } + + bool thisShouldBeMirrored = flowDirection == FlowDirection.RightToLeft && !bypassFlowDirectionPolicies; + bool parentShouldBeMirrored = parentFlowDirection == FlowDirection.RightToLeft && !parentBypassFlowDirectionPolicies; + + bool shouldApplyMirrorTransform = thisShouldBeMirrored != parentShouldBeMirrored; + + HasMirrorTransform = shouldApplyMirrorTransform; + } } } diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs index 6c74eb6b91..f2b808fe0d 100644 --- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs @@ -14,6 +14,15 @@ namespace Avalonia.Controls /// /// A control to allow the user to select a date /// + [TemplatePart("ButtonContentGrid", typeof(Grid))] + [TemplatePart("DayText", typeof(TextBlock))] + [TemplatePart("FirstSpacer", typeof(Rectangle))] + [TemplatePart("FlyoutButton", typeof(Button))] + [TemplatePart("MonthText", typeof(TextBlock))] + [TemplatePart("PickerPresenter", typeof(DatePickerPresenter))] + [TemplatePart("Popup", typeof(Popup))] + [TemplatePart("SecondSpacer", typeof(Rectangle))] + [TemplatePart("YearText", typeof(TextBlock))] [PseudoClasses(":hasnodate")] public class DatePicker : TemplatedControl { diff --git a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs index 0caba64758..0612efe14d 100644 --- a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs +++ b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs @@ -1,4 +1,5 @@ -using Avalonia.Controls.Primitives; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; using Avalonia.Controls.Shapes; using Avalonia.Input; using Avalonia.Interactivity; @@ -12,6 +13,23 @@ namespace Avalonia.Controls /// Defines the presenter used for selecting a date for a /// /// + [TemplatePart("AcceptButton", typeof(Button))] + [TemplatePart("DayDownButton", typeof(RepeatButton))] + [TemplatePart("DayHost", typeof(Panel))] + [TemplatePart("DaySelector", typeof(DateTimePickerPanel))] + [TemplatePart("DayUpButton", typeof(RepeatButton))] + [TemplatePart("DismissButton", typeof(Button))] + [TemplatePart("FirstSpacer", typeof(Rectangle))] + [TemplatePart("MonthDownButton", typeof(RepeatButton))] + [TemplatePart("MonthHost", typeof(Panel))] + [TemplatePart("MonthSelector", typeof(DateTimePickerPanel))] + [TemplatePart("MonthUpButton", typeof(RepeatButton))] + [TemplatePart("PickerContainer", typeof(Grid))] + [TemplatePart("SecondSpacer", typeof(Rectangle))] + [TemplatePart("YearDownButton", typeof(RepeatButton))] + [TemplatePart("YearHost", typeof(Panel))] + [TemplatePart("YearSelector", typeof(DateTimePickerPanel))] + [TemplatePart("YearUpButton", typeof(RepeatButton))] public class DatePickerPresenter : PickerPresenterBase { /// diff --git a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs index a923c44d05..667f994a1d 100644 --- a/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs +++ b/src/Avalonia.Controls/DateTimePickers/DateTimePickerPanel.cs @@ -1,7 +1,9 @@ using System; using System.Globalization; using System.Linq; +using Avalonia.Controls.Presenters; using Avalonia.Input; +using Avalonia.Input.GestureRecognizers; using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.VisualTree; @@ -60,6 +62,7 @@ namespace Avalonia.Controls.Primitives private Vector _offset; private bool _hasInit; private bool _suppressUpdateOffset; + private ScrollContentPresenter? _parentScroller; public DateTimePickerPanel() { @@ -255,6 +258,8 @@ namespace Avalonia.Controls.Primitives _suppressUpdateOffset = true; SelectedValue = (int)newSel * Increment + MinimumValue; _suppressUpdateOffset = false; + + System.Diagnostics.Debug.WriteLine($"Offset: {_offset} ItemHeight: {ItemHeight}"); } } @@ -270,7 +275,7 @@ namespace Avalonia.Controls.Primitives public Size Extent => _extent; - public Size Viewport => new Size(0, ItemHeight); + public Size Viewport => Bounds.Size; public event EventHandler? ScrollInvalidated; @@ -341,6 +346,20 @@ namespace Avalonia.Controls.Primitives return finalSize; } + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + _parentScroller = this.GetVisualParent() as ScrollContentPresenter; + _parentScroller?.AddHandler(Gestures.ScrollGestureEndedEvent, OnScrollGestureEnded); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + _parentScroller?.RemoveHandler(Gestures.ScrollGestureEndedEvent, OnScrollGestureEnded); + _parentScroller = null; + } + protected override void OnKeyDown(KeyEventArgs e) { switch (e.Key) @@ -554,5 +573,15 @@ namespace Avalonia.Controls.Primitives { ScrollInvalidated?.Invoke(this, e); } + + private void OnScrollGestureEnded(object? sender, ScrollGestureEndedEventArgs e) + { + var snapY = Math.Round(Offset.Y / ItemHeight) * ItemHeight; + + if (snapY != Offset.Y) + { + Offset = Offset.WithY(snapY); + } + } } } diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs index 3aab49d556..f04c79505e 100644 --- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs @@ -12,6 +12,18 @@ namespace Avalonia.Controls /// /// A control to allow the user to select a time. /// + [TemplatePart("FirstColumnDivider", typeof(Rectangle))] + [TemplatePart("FirstPickerHost", typeof(Border))] + [TemplatePart("FlyoutButton", typeof(Button))] + [TemplatePart("FlyoutButtonContentGrid", typeof(Grid))] + [TemplatePart("HourTextBlock", typeof(TextBlock))] + [TemplatePart("MinuteTextBlock", typeof(TextBlock))] + [TemplatePart("PeriodTextBlock", typeof(TextBlock))] + [TemplatePart("PickerPresenter", typeof(TimePickerPresenter))] + [TemplatePart("Popup", typeof(Popup))] + [TemplatePart("SecondColumnDivider", typeof(Rectangle))] + [TemplatePart("SecondPickerHost", typeof(Border))] + [TemplatePart("ThirdPickerHost", typeof(Border))] [PseudoClasses(":hasnotime")] public class TimePicker : TemplatedControl { diff --git a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs index 30b6144388..7f2abb7e98 100644 --- a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs +++ b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs @@ -1,4 +1,5 @@ -using Avalonia.Controls.Primitives; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; using Avalonia.Controls.Shapes; using Avalonia.Input; using Avalonia.Interactivity; @@ -10,6 +11,20 @@ namespace Avalonia.Controls /// Defines the presenter used for selecting a time. Intended for use with /// but can be used independently /// + [TemplatePart("AcceptButton", typeof(Button))] + [TemplatePart("DismissButton", typeof(Button))] + [TemplatePart("HourDownButton", typeof(RepeatButton))] + [TemplatePart("HourSelector", typeof(DateTimePickerPanel))] + [TemplatePart("HourUpButton", typeof(RepeatButton))] + [TemplatePart("MinuteDownButton", typeof(RepeatButton))] + [TemplatePart("MinuteSelector", typeof(DateTimePickerPanel))] + [TemplatePart("MinuteUpButton", typeof(RepeatButton))] + [TemplatePart("PeriodDownButton", typeof(RepeatButton))] + [TemplatePart("PeriodHost", typeof(Panel))] + [TemplatePart("PeriodSelector", typeof(DateTimePickerPanel))] + [TemplatePart("PeriodUpButton", typeof(RepeatButton))] + [TemplatePart("PickerContainer", typeof(Grid))] + [TemplatePart("SecondSpacer", typeof(Rectangle))] public class TimePickerPresenter : PickerPresenterBase { /// diff --git a/src/Avalonia.Controls/Documents/TextElement.cs b/src/Avalonia.Controls/Documents/TextElement.cs index 4083524881..d8e13554b5 100644 --- a/src/Avalonia.Controls/Documents/TextElement.cs +++ b/src/Avalonia.Controls/Documents/TextElement.cs @@ -13,37 +13,60 @@ namespace Avalonia.Controls.Documents /// Defines the property. /// public static readonly StyledProperty BackgroundProperty = - AvaloniaProperty.Register(nameof(Background), inherits: true); + Border.BackgroundProperty.AddOwner(); /// /// Defines the property. /// public static readonly AttachedProperty FontFamilyProperty = - TextBlock.FontFamilyProperty.AddOwner(); + AvaloniaProperty.RegisterAttached( + nameof(FontFamily), + defaultValue: FontFamily.Default, + inherits: true); /// /// Defines the property. /// public static readonly AttachedProperty FontSizeProperty = - TextBlock.FontSizeProperty.AddOwner(); + AvaloniaProperty.RegisterAttached( + nameof(FontSize), + defaultValue: 12, + inherits: true); /// /// Defines the property. /// public static readonly AttachedProperty FontStyleProperty = - TextBlock.FontStyleProperty.AddOwner(); + AvaloniaProperty.RegisterAttached( + nameof(FontStyle), + inherits: true); /// /// Defines the property. /// public static readonly AttachedProperty FontWeightProperty = - TextBlock.FontWeightProperty.AddOwner(); + AvaloniaProperty.RegisterAttached( + nameof(FontWeight), + inherits: true, + defaultValue: FontWeight.Normal); + + /// + /// Defines the property. + /// + public static readonly AttachedProperty FontStretchProperty = + AvaloniaProperty.RegisterAttached( + nameof(FontStretch), + inherits: true, + defaultValue: FontStretch.Normal); /// /// Defines the property. /// public static readonly AttachedProperty ForegroundProperty = - TextBlock.ForegroundProperty.AddOwner(); + AvaloniaProperty.RegisterAttached( + nameof(Foreground), + Brushes.Black, + inherits: true); /// /// Gets or sets a brush used to paint the control's background. @@ -90,6 +113,15 @@ namespace Avalonia.Controls.Documents set { SetValue(FontWeightProperty, value); } } + /// + /// Gets or sets the font stretch. + /// + public FontStretch FontStretch + { + get { return GetValue(FontStretchProperty); } + set { SetValue(FontStretchProperty, value); } + } + /// /// Gets or sets a brush used to paint the text. /// @@ -98,7 +130,127 @@ namespace Avalonia.Controls.Documents get { return GetValue(ForegroundProperty); } set { SetValue(ForegroundProperty, value); } } - + + /// + /// Gets the value of the attached on a control. + /// + /// The control. + /// The font family. + public static FontFamily GetFontFamily(Control control) + { + return control.GetValue(FontFamilyProperty); + } + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetFontFamily(Control control, FontFamily value) + { + control.SetValue(FontFamilyProperty, value); + } + + /// + /// Gets the value of the attached on a control. + /// + /// The control. + /// The font size. + public static double GetFontSize(Control control) + { + return control.GetValue(FontSizeProperty); + } + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetFontSize(Control control, double value) + { + control.SetValue(FontSizeProperty, value); + } + + /// + /// Gets the value of the attached on a control. + /// + /// The control. + /// The font style. + public static FontStyle GetFontStyle(Control control) + { + return control.GetValue(FontStyleProperty); + } + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetFontStyle(Control control, FontStyle value) + { + control.SetValue(FontStyleProperty, value); + } + + /// + /// Gets the value of the attached on a control. + /// + /// The control. + /// The font weight. + public static FontWeight GetFontWeight(Control control) + { + return control.GetValue(FontWeightProperty); + } + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetFontWeight(Control control, FontWeight value) + { + control.SetValue(FontWeightProperty, value); + } + + /// + /// Gets the value of the attached on a control. + /// + /// The control. + /// The font stretch. + public static FontStretch GetFontStretch(Control control) + { + return control.GetValue(FontStretchProperty); + } + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetFontStretch(Control control, FontStretch value) + { + control.SetValue(FontStretchProperty, value); + } + + /// + /// Gets the value of the attached on a control. + /// + /// The control. + /// The foreground. + public static IBrush? GetForeground(Control control) + { + return control.GetValue(ForegroundProperty); + } + + /// + /// Sets the value of the attached on a control. + /// + /// The control. + /// The property value to set. + public static void SetForeground(Control control, IBrush? value) + { + control.SetValue(ForegroundProperty, value); + } + /// /// Raised when the visual representation of the text element changes. /// @@ -115,6 +267,7 @@ namespace Avalonia.Controls.Documents case nameof(FontSize): case nameof(FontStyle): case nameof(FontWeight): + case nameof(FontStretch): case nameof(Foreground): Invalidate(); break; diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs deleted file mode 100644 index 4e17f5bff5..0000000000 --- a/src/Avalonia.Controls/DropDown.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Avalonia.Logging; -using Avalonia.Styling; - -namespace Avalonia.Controls -{ - [Obsolete("Use ComboBox")] - public class DropDown : ComboBox, IStyleable - { - public DropDown() - { - Logger.TryGet(LogEventLevel.Warning, LogArea.Control)?.Log(this, "DropDown is deprecated: Use ComboBox"); - } - - Type IStyleable.StyleKey => typeof(ComboBox); - } - - [Obsolete("Use ComboBoxItem")] - public class DropDownItem : ComboBoxItem, IStyleable - { - public DropDownItem() - { - Logger.TryGet(LogEventLevel.Warning, LogArea.Control)?.Log(this, "DropDownItem is deprecated: Use ComboBoxItem"); - } - - Type IStyleable.StyleKey => typeof(ComboBoxItem); - } -} diff --git a/src/Avalonia.Controls/DropDownButton.cs b/src/Avalonia.Controls/DropDownButton.cs new file mode 100644 index 0000000000..8dc40f42af --- /dev/null +++ b/src/Avalonia.Controls/DropDownButton.cs @@ -0,0 +1,15 @@ +namespace Avalonia.Controls +{ + /// + /// A button with an added drop-down chevron to visually indicate it has a flyout with additional actions. + /// + public class DropDownButton : Button + { + /// + /// Initializes a new instance of the class. + /// + public DropDownButton() + { + } + } +} diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs index 79a9e16879..a76dcbe9c8 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs @@ -82,7 +82,7 @@ namespace Avalonia.Controls.Generators { var toMove = _containers.Where(x => x.Key >= index) .OrderByDescending(x => x.Key) - .ToList(); + .ToArray(); foreach (var i in toMove) { @@ -111,7 +111,7 @@ namespace Avalonia.Controls.Generators } var toMove = _containers.Where(x => x.Key >= startingIndex) - .OrderBy(x => x.Key).ToList(); + .OrderBy(x => x.Key).ToArray(); foreach (var i in toMove) { @@ -122,9 +122,9 @@ namespace Avalonia.Controls.Generators Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result)); - if (toMove.Count > 0) + if (toMove.Length > 0) { - var containers = toMove.Select(x => x.Value).ToList(); + var containers = toMove.Select(x => x.Value).ToArray(); Recycled?.Invoke(this, new ItemContainerEventArgs(containers[0].Index, containers)); } } @@ -138,10 +138,10 @@ namespace Avalonia.Controls.Generators /// public virtual IEnumerable Clear() { - var result = Containers.ToList(); + var result = Containers.ToArray(); _containers.Clear(); - if (result.Count > 0) + if (result.Length > 0) { Dematerialized?.Invoke(this, new ItemContainerEventArgs(0, result)); } diff --git a/src/Avalonia.Controls/HotkeyManager.cs b/src/Avalonia.Controls/HotkeyManager.cs index d83b996aa0..bde1da509b 100644 --- a/src/Avalonia.Controls/HotkeyManager.cs +++ b/src/Avalonia.Controls/HotkeyManager.cs @@ -12,21 +12,61 @@ namespace Avalonia.Controls class HotkeyCommandWrapper : ICommand { - public HotkeyCommandWrapper(ICommandSource? control) + readonly WeakReference reference; + + public HotkeyCommandWrapper(IControl control) { - CommandSource = control; + reference = new WeakReference(control); } - public readonly ICommandSource? CommandSource; + public ICommand? GetCommand() + { + if (reference.Target is { } target) + { + if (target is ICommandSource commandSource && commandSource.Command is { } command) + { + return command; + } + else if (target is IClickableControl { }) + { + return this; + } + } + return null; + } - private ICommand? GetCommand() => CommandSource?.Command; + public bool CanExecute(object? parameter) + { + if (reference.Target is { } target) + { + if (target is ICommandSource commandSource && commandSource.Command is { } command) + { + return commandSource.IsEffectivelyEnabled + && command.CanExecute(commandSource.CommandParameter) == true; + } + else if (target is IClickableControl clickable) + { + return clickable.IsEffectivelyEnabled; + } + } + return false; + } - public bool CanExecute(object? parameter) => - CommandSource?.Command?.CanExecute(CommandSource.CommandParameter) == true - && CommandSource.IsEffectivelyEnabled; + public void Execute(object? parameter) + { + if (reference.Target is { } target) + { + if (target is ICommandSource commandSource && commandSource.Command is { } command) + { + command.Execute(commandSource.CommandParameter); + } + else if (target is IClickableControl { IsEffectivelyEnabled: true } clickable) + { + clickable.RaiseClick(); + } + } + } - public void Execute(object? parameter) => - GetCommand()?.Execute(CommandSource?.CommandParameter); #pragma warning disable 67 // Event not used public event EventHandler? CanExecuteChanged; @@ -47,7 +87,7 @@ namespace Avalonia.Controls public Manager(IControl control) { _control = control; - _wrapper = new HotkeyCommandWrapper(_control as ICommandSource); + _wrapper = new HotkeyCommandWrapper(_control); } public void Init() @@ -104,13 +144,14 @@ namespace Avalonia.Controls { HotKeyProperty.Changed.Subscribe(args => { - if (args.NewValue.Value is null) return; + if (args.NewValue.Value is null) + return; var control = args.Sender as IControl; - if (control is not ICommandSource) + if (control is not IClickableControl) { Logging.Logger.TryGet(Logging.LogEventLevel.Warning, Logging.LogArea.Control)?.Log(control, - $"The element {args.Sender.GetType().Name} does not implement ICommandSource and does not support binding a HotKey ({args.NewValue})."); + $"The element {args.Sender.GetType().Name} does not implement IClickableControl and does not support binding a HotKey ({args.NewValue})."); return; } diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index 3d67880638..7408bff902 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -66,6 +66,8 @@ namespace Avalonia.Controls set { SetValue(StretchDirectionProperty, value); } } + protected override bool BypassFlowDirectionPolicies => true; + /// /// Renders the control. /// diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 0cd72dc91c..ab236f703d 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -166,7 +166,7 @@ namespace Avalonia.Controls if (Presenter is IChildIndexProvider innerProvider) { innerProvider.ChildIndexChanged += PresenterChildIndexChanged; - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); } } @@ -508,7 +508,6 @@ namespace Avalonia.Controls do { result = container.GetControl(direction, c, wrap); - from = from ?? result; if (result != null && result.Focusable && diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs index 9b7ae0d324..79285bb86b 100644 --- a/src/Avalonia.Controls/ListBox.cs +++ b/src/Avalonia.Controls/ListBox.cs @@ -1,5 +1,6 @@ using System.Collections; using Avalonia.Controls.Generators; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Selection; @@ -12,6 +13,7 @@ namespace Avalonia.Controls /// /// An in which individual items can be selected. /// + [TemplatePart("PART_ScrollViewer", typeof(IScrollable))] public class ListBox : SelectingItemsControl { /// diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 34215e9713..955af8888b 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -20,8 +20,9 @@ namespace Avalonia.Controls /// /// A menu item control. /// + [TemplatePart("PART_Popup", typeof(Popup))] [PseudoClasses(":separator", ":icon", ":open", ":pressed", ":selected")] - public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable, ICommandSource + public class MenuItem : HeaderedSelectingItemsControl, IMenuItem, ISelectable, ICommandSource, IClickableControl { /// /// Defines the property. @@ -643,7 +644,9 @@ namespace Avalonia.Controls /// The property change event. private void IsSelectedChanged(AvaloniaPropertyChangedEventArgs e) { - if ((bool)e.NewValue!) + var parentMenu = Parent as Menu; + + if ((bool)e.NewValue! && (parentMenu is null || parentMenu.IsOpen)) { Focus(); } @@ -704,6 +707,14 @@ namespace Avalonia.Controls void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e); + void IClickableControl.RaiseClick() + { + if (IsEffectivelyEnabled) + { + RaiseEvent(new RoutedEventArgs(ClickEvent)); + } + } + /// /// A dependency resolver which returns a . /// diff --git a/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs b/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs index 9499995da3..d6b82a8f8a 100644 --- a/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs +++ b/src/Avalonia.Controls/Notifications/WindowNotificationManager.cs @@ -14,6 +14,7 @@ namespace Avalonia.Controls.Notifications /// /// An that displays notifications in a . /// + [TemplatePart("PART_Items", typeof(Panel))] [PseudoClasses(":topleft", ":topright", ":bottomleft", ":bottomright")] public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager, ICustomSimpleHitTest { diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index f67377b310..fbbaab6182 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.IO; using System.Linq; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Input; @@ -15,6 +16,8 @@ namespace Avalonia.Controls /// /// Control that represents a TextBox with button spinners that allow incrementing and decrementing numeric values. /// + [TemplatePart("PART_Spinner", typeof(Spinner))] + [TemplatePart("PART_TextBox", typeof(TextBox))] public class NumericUpDown : TemplatedControl { /// @@ -1051,7 +1054,7 @@ namespace Avalonia.Controls var currentValueTextSpecialCharacters = currentValueText.Where(c => !char.IsDigit(c)); var textSpecialCharacters = text.Where(c => !char.IsDigit(c)); // same non-digit characters on currentValueText and new text => remove them on new Text to parse it again. - if (currentValueTextSpecialCharacters.Except(textSpecialCharacters).ToList().Count == 0) + if (!currentValueTextSpecialCharacters.Except(textSpecialCharacters).Any()) { foreach (var character in textSpecialCharacters) { diff --git a/src/Avalonia.Controls/Panel.cs b/src/Avalonia.Controls/Panel.cs index 482a7fab84..2230b4b0d2 100644 --- a/src/Avalonia.Controls/Panel.cs +++ b/src/Avalonia.Controls/Panel.cs @@ -147,7 +147,7 @@ namespace Avalonia.Controls throw new NotSupportedException(); } - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); InvalidateMeasureOnChildrenChanged(); } diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 23ecdb2e7b..6e9ac537f1 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -149,13 +149,18 @@ namespace Avalonia.Controls.Platform case Key.Up: case Key.Down: { - if (item?.IsTopLevel == true) + if (item?.IsTopLevel == true && item.HasSubMenu) { - if (item.HasSubMenu && !item.IsSubMenuOpen) + if (!item.IsSubMenuOpen) { Open(item, true); - e.Handled = true; } + else + { + item.MoveSelection(NavigationDirection.First, true); + } + + e.Handled = true; } else { @@ -247,7 +252,8 @@ namespace Avalonia.Controls.Platform // new menu. if (item.IsSubMenuOpen && item.Parent is IMenu && - item.Parent.SelectedItem is object) + item.Parent.SelectedItem is object && + item.Parent.SelectedItem != item) { item.Close(); Open(item.Parent.SelectedItem, true); diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 93acd88fb1..8229f25a07 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -1,5 +1,6 @@ using System; +using Avalonia.Controls.Documents; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; @@ -46,7 +47,73 @@ namespace Avalonia.Controls.Presenters /// public static readonly StyledProperty BoxShadowProperty = Border.BoxShadowProperty.AddOwner(); - + + /// + /// Defines the property. + /// + public static readonly AttachedProperty ForegroundProperty = + TextElement.ForegroundProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly AttachedProperty FontFamilyProperty = + TextElement.FontFamilyProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly AttachedProperty FontSizeProperty = + TextElement.FontSizeProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly AttachedProperty FontStyleProperty = + TextElement.FontStyleProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly AttachedProperty FontWeightProperty = + TextElement.FontWeightProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly AttachedProperty FontStretchProperty = + TextElement.FontStretchProperty.AddOwner(); + + /// + /// Defines the property + /// + public static readonly AttachedProperty TextAlignmentProperty = + TextBlock.TextAlignmentProperty.AddOwner(); + + /// + /// Defines the property + /// + public static readonly AttachedProperty TextWrappingProperty = + TextBlock.TextWrappingProperty.AddOwner(); + + /// + /// Defines the property + /// + public static readonly AttachedProperty TextTrimmingProperty = + TextBlock.TextTrimmingProperty.AddOwner(); + + /// + /// Defines the property + /// + public static readonly AttachedProperty LineHeightProperty = + TextBlock.LineHeightProperty.AddOwner(); + + /// + /// Defines the property + /// + public static readonly AttachedProperty MaxLinesProperty = + TextBlock.MaxLinesProperty.AddOwner(); + /// /// Defines the property. /// @@ -159,6 +226,105 @@ namespace Avalonia.Controls.Presenters set => SetValue(BoxShadowProperty, value); } + /// + /// Gets or sets a brush used to paint the text. + /// + public IBrush? Foreground + { + get => GetValue(ForegroundProperty); + set => SetValue(ForegroundProperty, value); + } + + /// + /// Gets or sets the font family. + /// + public FontFamily FontFamily + { + get => GetValue(FontFamilyProperty); + set => SetValue(FontFamilyProperty, value); + } + + /// + /// Gets or sets the font size. + /// + public double FontSize + { + get => GetValue(FontSizeProperty); + set => SetValue(FontSizeProperty, value); + } + + /// + /// Gets or sets the font style. + /// + public FontStyle FontStyle + { + get => GetValue(FontStyleProperty); + set => SetValue(FontStyleProperty, value); + } + + /// + /// Gets or sets the font weight. + /// + public FontWeight FontWeight + { + get => GetValue(FontWeightProperty); + set => SetValue(FontWeightProperty, value); + } + + /// + /// Gets or sets the font stretch. + /// + public FontStretch FontStretch + { + get => GetValue(FontStretchProperty); + set => SetValue(FontStretchProperty, value); + } + + /// + /// Gets or sets the text alignment + /// + public TextAlignment TextAlignment + { + get => GetValue(TextAlignmentProperty); + set => SetValue(TextAlignmentProperty, value); + } + + /// + /// Gets or sets the text wrapping + /// + public TextWrapping TextWrapping + { + get => GetValue(TextWrappingProperty); + set => SetValue(TextWrappingProperty, value); + } + + /// + /// Gets or sets the text trimming + /// + public TextTrimming TextTrimming + { + get => GetValue(TextTrimmingProperty); + set => SetValue(TextTrimmingProperty, value); + } + + /// + /// Gets or sets the line height + /// + public double LineHeight + { + get => GetValue(LineHeightProperty); + set => SetValue(LineHeightProperty, value); + } + + /// + /// Gets or sets the max lines + /// + public int MaxLines + { + get => GetValue(MaxLinesProperty); + set => SetValue(MaxLinesProperty, value); + } + /// /// Gets the control displayed by the presenter. /// diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs index f938c8d437..2821fa8cf0 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs @@ -158,7 +158,7 @@ namespace Avalonia.Controls.Presenters { ItemsChanged(e); - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); } } diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 97fb4c3f43..a8bffcc842 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -389,7 +389,7 @@ namespace Avalonia.Controls.Presenters { var logicalUnits = delta.Y / logicalScrollItemSize.Y; delta = delta.WithY(delta.Y - logicalUnits * logicalScrollItemSize.Y); - dy = logicalUnits * scrollable!.ScrollSize.Height; + dy = logicalUnits; } else dy = delta.Y; @@ -407,7 +407,7 @@ namespace Avalonia.Controls.Presenters { var logicalUnits = delta.X / logicalScrollItemSize.X; delta = delta.WithX(delta.X - logicalUnits * logicalScrollItemSize.X); - dx = logicalUnits * scrollable!.ScrollSize.Width; + dx = logicalUnits; } else dx = delta.X; diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 10ce31088a..7f2dde7c1e 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -8,6 +8,7 @@ using Avalonia.Utilities; using Avalonia.VisualTree; using Avalonia.Layout; using Avalonia.Media.Immutable; +using Avalonia.Controls.Documents; namespace Avalonia.Controls.Presenters { @@ -121,8 +122,8 @@ namespace Avalonia.Controls.Presenters /// public FontFamily FontFamily { - get => TextBlock.GetFontFamily(this); - set => TextBlock.SetFontFamily(this, value); + get => TextElement.GetFontFamily(this); + set => TextElement.SetFontFamily(this, value); } /// @@ -130,8 +131,8 @@ namespace Avalonia.Controls.Presenters /// public double FontSize { - get => TextBlock.GetFontSize(this); - set => TextBlock.SetFontSize(this, value); + get => TextElement.GetFontSize(this); + set => TextElement.SetFontSize(this, value); } /// @@ -139,8 +140,8 @@ namespace Avalonia.Controls.Presenters /// public FontStyle FontStyle { - get => TextBlock.GetFontStyle(this); - set => TextBlock.SetFontStyle(this, value); + get => TextElement.GetFontStyle(this); + set => TextElement.SetFontStyle(this, value); } /// @@ -148,8 +149,8 @@ namespace Avalonia.Controls.Presenters /// public FontWeight FontWeight { - get => TextBlock.GetFontWeight(this); - set => TextBlock.SetFontWeight(this, value); + get => TextElement.GetFontWeight(this); + set => TextElement.SetFontWeight(this, value); } /// @@ -157,8 +158,8 @@ namespace Avalonia.Controls.Presenters /// public FontStretch FontStretch { - get => TextBlock.GetFontStretch(this); - set => TextBlock.SetFontStretch(this, value); + get => TextElement.GetFontStretch(this); + set => TextElement.SetFontStretch(this, value); } /// @@ -166,8 +167,8 @@ namespace Avalonia.Controls.Presenters /// public IBrush? Foreground { - get => TextBlock.GetForeground(this); - set => TextBlock.SetForeground(this, value); + get => TextElement.GetForeground(this); + set => TextElement.SetForeground(this, value); } /// @@ -281,6 +282,8 @@ namespace Avalonia.Controls.Presenters } } + protected override bool BypassFlowDirectionPolicies => true; + /// /// Creates the used to render the text. /// @@ -353,7 +356,7 @@ namespace Avalonia.Controls.Presenters foreach (var rect in rects) { - context.FillRectangle(selectionBrush, rect); + context.FillRectangle(selectionBrush, PixelRect.FromRect(rect, 1).ToRect(1)); } } @@ -528,9 +531,9 @@ namespace Avalonia.Controls.Presenters { return finalSize; } - - _constraint = finalSize; - + + _constraint = new Size(finalSize.Width, Math.Ceiling(finalSize.Height)); + _textLayout = null; return finalSize; @@ -637,14 +640,7 @@ namespace Avalonia.Controls.Presenters if (Text is null) { return default; - } - - if (FlowDirection == FlowDirection.RightToLeft) - { - direction = direction == LogicalDirection.Forward ? - LogicalDirection.Backward : - LogicalDirection.Forward; - } + } var characterHit = _lastCharacterHit; var caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; @@ -666,7 +662,7 @@ namespace Avalonia.Controls.Presenters caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - if (textLine.NewLineLength > 0 && caretIndex == textLine.TextRange.Start + textLine.TextRange.Length) + if (textLine.NewLineLength > 0 && caretIndex == textLine.FirstTextSourceIndex + textLine.Length) { characterHit = new CharacterHit(caretIndex); } @@ -678,7 +674,7 @@ namespace Avalonia.Controls.Presenters break; } - if (caretIndex - textLine.NewLineLength == textLine.TextRange.Start + textLine.TextRange.Length) + if (caretIndex - textLine.NewLineLength == textLine.FirstTextSourceIndex + textLine.Length) { break; } @@ -719,6 +715,13 @@ namespace Avalonia.Controls.Presenters public void MoveCaretHorizontal(LogicalDirection direction = LogicalDirection.Forward) { + if (FlowDirection == FlowDirection.RightToLeft) + { + direction = direction == LogicalDirection.Forward ? + LogicalDirection.Backward : + LogicalDirection.Forward; + } + var characterHit = GetNextCharacterHit(direction); UpdateCaret(characterHit); @@ -779,19 +782,25 @@ namespace Avalonia.Controls.Presenters switch (change.Property.Name) { - case nameof (TextBlock.Foreground): - case nameof (TextBlock.FontSize): - case nameof (TextBlock.FontStyle): - case nameof (TextBlock.FontWeight): - case nameof (TextBlock.FontFamily): + case nameof (Foreground): + case nameof (FontSize): + case nameof (FontStyle): + case nameof (FontWeight): + case nameof (FontFamily): + case nameof (FontStretch): + case nameof (Text): case nameof (TextAlignment): case nameof (TextWrapping): + case nameof (SelectionStart): case nameof (SelectionEnd): case nameof (SelectionForegroundBrush): + case nameof (PasswordChar): case nameof (RevealPassword): + + case nameof(FlowDirection): { InvalidateTextLayout(); break; diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs index 6251d5cda7..6ac544e0fe 100644 --- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs @@ -76,7 +76,7 @@ namespace Avalonia.Controls.Primitives Rect? rect = null) { _positionerParameters.ConfigurePosition((TopLevel)_overlayLayer.GetVisualRoot()!, target, placement, offset, anchor, - gravity, constraintAdjustment, rect); + gravity, constraintAdjustment, rect, FlowDirection); UpdatePosition(); } diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs index 340076a407..8daf1ac68a 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs @@ -46,6 +46,7 @@ Copyright © 2019 Nikita Tsukanov using System; using Avalonia.VisualTree; +using Avalonia.Media; namespace Avalonia.Controls.Primitives.PopupPositioning { @@ -444,7 +445,8 @@ namespace Avalonia.Controls.Primitives.PopupPositioning TopLevel topLevel, IVisual target, PlacementMode placement, Point offset, PopupAnchor anchor, PopupGravity gravity, - PopupPositionerConstraintAdjustment constraintAdjustment, Rect? rect) + PopupPositionerConstraintAdjustment constraintAdjustment, Rect? rect, + FlowDirection flowDirection) { // We need a better way for tracking the last pointer position #pragma warning disable CS0618 // Type or member is obsolete @@ -503,6 +505,32 @@ namespace Avalonia.Controls.Primitives.PopupPositioning else throw new InvalidOperationException("Invalid value for Popup.PlacementMode"); } + + // Invert coordinate system if FlowDirection is RTL + if (flowDirection == FlowDirection.RightToLeft) + { + if ((positionerParameters.Anchor & PopupAnchor.Right) == PopupAnchor.Right) + { + positionerParameters.Anchor ^= PopupAnchor.Right; + positionerParameters.Anchor |= PopupAnchor.Left; + } + else if ((positionerParameters.Anchor & PopupAnchor.Left) == PopupAnchor.Left) + { + positionerParameters.Anchor ^= PopupAnchor.Left; + positionerParameters.Anchor |= PopupAnchor.Right; + } + + if ((positionerParameters.Gravity & PopupGravity.Right) == PopupGravity.Right) + { + positionerParameters.Gravity ^= PopupGravity.Right; + positionerParameters.Gravity |= PopupGravity.Left; + } + else if ((positionerParameters.Gravity & PopupGravity.Left) == PopupGravity.Left) + { + positionerParameters.Gravity ^= PopupGravity.Left; + positionerParameters.Gravity |= PopupGravity.Right; + } + } } } diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs index 91ed5d975d..51a21323d9 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs @@ -23,8 +23,9 @@ namespace Avalonia.Controls.Primitives.PopupPositioning public IReadOnlyList Screens => - _parent.Screen.AllScreens.Select(s => new ManagedPopupPositionerScreenInfo( - s.Bounds.ToRect(1), s.WorkingArea.ToRect(1))).ToList(); + _parent.Screen.AllScreens + .Select(s => new ManagedPopupPositionerScreenInfo(s.Bounds.ToRect(1), s.WorkingArea.ToRect(1))) + .ToArray(); public Rect ParentClientAreaScreenGeometry { diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 2f6df862cf..abf56e5420 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -17,7 +17,6 @@ namespace Avalonia.Controls.Primitives /// public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost { - private readonly TopLevel _parent; private PopupPositionerParameters _positionerParameters; /// @@ -47,7 +46,7 @@ namespace Avalonia.Controls.Primitives public PopupRoot(TopLevel parent, IPopupImpl impl, IAvaloniaDependencyResolver? dependencyResolver) : base(impl, dependencyResolver) { - _parent = parent; + ParentTopLevel = parent; } /// @@ -73,6 +72,8 @@ namespace Avalonia.Controls.Primitives /// IStyleHost? IStyleHost.StylingParent => Parent; + public TopLevel ParentTopLevel { get; } + /// public void Dispose() { @@ -91,8 +92,8 @@ namespace Avalonia.Controls.Primitives PopupPositionerConstraintAdjustment constraintAdjustment = PopupPositionerConstraintAdjustment.All, Rect? rect = null) { - _positionerParameters.ConfigurePosition(_parent, target, - placement, offset, anchor, gravity, constraintAdjustment, rect); + _positionerParameters.ConfigurePosition(ParentTopLevel, target, + placement, offset, anchor, gravity, constraintAdjustment, rect, FlowDirection); if (_positionerParameters.Size != default) UpdatePosition(); diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs index 8460fe3017..6a30097fbb 100644 --- a/src/Avalonia.Controls/Primitives/ScrollBar.cs +++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs @@ -22,6 +22,10 @@ namespace Avalonia.Controls.Primitives /// /// A scrollbar control. /// + [TemplatePart("PART_LineDownButton", typeof(Button))] + [TemplatePart("PART_LineUpButton", typeof(Button))] + [TemplatePart("PART_PageDownButton", typeof(Button))] + [TemplatePart("PART_PageUpButton", typeof(Button))] [PseudoClasses(":vertical", ":horizontal")] public class ScrollBar : RangeBase { diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index c690726e71..cec02c7ae9 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -292,11 +292,11 @@ namespace Avalonia.Controls.Primitives "collection is different to the Items on the control."); } - var oldSelection = _selection?.SelectedItems.ToList(); + var oldSelection = _selection?.SelectedItems.ToArray(); DeinitializeSelectionModel(_selection); _selection = value; - if (oldSelection?.Count > 0) + if (oldSelection?.Length > 0) { RaiseEvent(new SelectionChangedEventArgs( SelectionChangedEvent, @@ -845,8 +845,8 @@ namespace Avalonia.Controls.Primitives { var ev = new SelectionChangedEventArgs( SelectionChangedEvent, - e.DeselectedItems.ToList(), - e.SelectedItems.ToList()); + e.DeselectedItems.ToArray(), + e.SelectedItems.ToArray()); RaiseEvent(ev); } } @@ -988,7 +988,7 @@ namespace Avalonia.Controls.Primitives RaiseEvent(new SelectionChangedEventArgs( SelectionChangedEvent, Array.Empty(), - Selection.SelectedItems.ToList())); + Selection.SelectedItems.ToArray())); } } diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index af681d6930..6e4ae748d9 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Controls.Documents; using Avalonia.Controls.Templates; using Avalonia.Interactivity; using Avalonia.Logging; @@ -41,31 +42,37 @@ namespace Avalonia.Controls.Primitives /// Defines the property. /// public static readonly StyledProperty FontFamilyProperty = - TextBlock.FontFamilyProperty.AddOwner(); + TextElement.FontFamilyProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty FontSizeProperty = - TextBlock.FontSizeProperty.AddOwner(); + TextElement.FontSizeProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty FontStyleProperty = - TextBlock.FontStyleProperty.AddOwner(); + TextElement.FontStyleProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty FontWeightProperty = - TextBlock.FontWeightProperty.AddOwner(); + TextElement.FontWeightProperty.AddOwner(); + + /// + /// Defines the property. + /// + public static readonly StyledProperty FontStretchProperty = + TextElement.FontStretchProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty ForegroundProperty = - TextBlock.ForegroundProperty.AddOwner(); + TextElement.ForegroundProperty.AddOwner(); /// /// Defines the property. @@ -185,6 +192,15 @@ namespace Avalonia.Controls.Primitives set { SetValue(FontWeightProperty, value); } } + /// + /// Gets or sets the font stretch used to draw the control's text. + /// + public FontStretch FontStretch + { + get { return GetValue(FontStretchProperty); } + set { SetValue(FontStretchProperty, value); } + } + /// /// Gets or sets the brush used to draw the control's text and other foreground elements. /// @@ -340,6 +356,11 @@ namespace Avalonia.Controls.Primitives base.OnDetachedFromLogicalTree(e); } + /// + /// Called when the control's template is applied. + /// In simple terms, this means the method is called just before the control is displayed. + /// + /// The event args. protected virtual void OnApplyTemplate(TemplateAppliedEventArgs e) { } diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs index 1d8d6aef45..a4f2cc799a 100644 --- a/src/Avalonia.Controls/ProgressBar.cs +++ b/src/Avalonia.Controls/ProgressBar.cs @@ -10,6 +10,7 @@ namespace Avalonia.Controls /// /// A control used to indicate the progress of an operation. /// + [TemplatePart("PART_Indicator", typeof(Border))] [PseudoClasses(":vertical", ":horizontal", ":indeterminate")] public class ProgressBar : RangeBase { diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs index 73bb827ad9..535f9ae43e 100644 --- a/src/Avalonia.Controls/ScrollViewer.cs +++ b/src/Avalonia.Controls/ScrollViewer.cs @@ -1,6 +1,7 @@ using System; using System.Reactive.Linq; using Avalonia.Automation.Peers; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Input; @@ -11,6 +12,8 @@ namespace Avalonia.Controls /// /// A control which scrolls its content if the content is bigger than the space available. /// + [TemplatePart("PART_HorizontalScrollBar", typeof(ScrollBar))] + [TemplatePart("PART_VerticalScrollBar", typeof(ScrollBar))] public class ScrollViewer : ContentControl, IScrollable, IScrollAnchorProvider { /// diff --git a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs index 40c6f63ed8..d92ffb0d1a 100644 --- a/src/Avalonia.Controls/Selection/InternalSelectionModel.cs +++ b/src/Avalonia.Controls/Selection/InternalSelectionModel.cs @@ -182,8 +182,8 @@ namespace Avalonia.Controls.Selection try { var items = WritableSelectedItems; - var deselected = e.DeselectedItems.ToList(); - var selected = e.SelectedItems.ToList(); + var deselected = e.DeselectedItems.ToArray(); + var selected = e.SelectedItems.ToArray(); _ignoreSelectedItemsChanges = true; diff --git a/src/Avalonia.Controls/Shapes/Rectangle.cs b/src/Avalonia.Controls/Shapes/Rectangle.cs index f3db2644a9..d60180bab4 100644 --- a/src/Avalonia.Controls/Shapes/Rectangle.cs +++ b/src/Avalonia.Controls/Shapes/Rectangle.cs @@ -2,19 +2,139 @@ using Avalonia.Media; namespace Avalonia.Controls.Shapes { + /// + /// Represents a rectangle with optional rounded corners. + /// public class Rectangle : Shape { + private const double PiOver2 = 1.57079633; // 90 deg to rad + + /// + /// Defines the property. + /// + public static readonly StyledProperty RadiusXProperty = + AvaloniaProperty.Register(nameof(RadiusX)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty RadiusYProperty = + AvaloniaProperty.Register(nameof(RadiusY)); + static Rectangle() { - AffectsGeometry(BoundsProperty, StrokeThicknessProperty); + AffectsGeometry( + BoundsProperty, + RadiusXProperty, + RadiusYProperty, + StrokeThicknessProperty); + } + + /// + /// Gets or sets the radius on the X-axis used to round the corners of the rectangle. + /// Corner radii are represented by an ellipse so this is the X-axis width of the ellipse. + /// + public double RadiusX + { + get => GetValue(RadiusXProperty); + set => SetValue(RadiusXProperty, value); + } + + /// + /// Gets or sets the radius on the Y-axis used to round the corners of the rectangle. + /// Corner radii are represented by an ellipse so this is the Y-axis height of the ellipse. + /// + public double RadiusY + { + get => GetValue(RadiusYProperty); + set => SetValue(RadiusYProperty, value); } + /// protected override Geometry CreateDefiningGeometry() { - var rect = new Rect(Bounds.Size).Deflate(StrokeThickness / 2); - return new RectangleGeometry(rect); + // TODO: If RectangleGeometry ever supports RadiusX/Y like in WPF, + // this code can be removed/combined with that implementation + + double x = RadiusX; + double y = RadiusY; + + if (x == 0 && y == 0) + { + // Optimization when there are no corner radii + var rect = new Rect(Bounds.Size).Deflate(StrokeThickness / 2); + return new RectangleGeometry(rect); + } + else + { + var rect = new Rect(Bounds.Size).Deflate(StrokeThickness / 2); + var geometry = new StreamGeometry(); + var arcSize = new Size(x, y); + + using (StreamGeometryContext context = geometry.Open()) + { + // The rectangle is constructed as follows: + // + // (origin) + // Corner 4 Corner 1 + // Top/Left Line 1 Top/Right + // \_ __________ _/ + // | | + // Line 4 | | Line 2 + // _ |__________| _ + // / Line 3 \ + // Corner 3 Corner 2 + // Bottom/Left Bottom/Right + // + // - Lines 1,3 follow the deflated rectangle bounds minus RadiusX + // - Lines 2,4 follow the deflated rectangle bounds minus RadiusY + // - All corners are constructed using elliptical arcs + + // Line 1 + Corner 1 + context.BeginFigure(new Point(rect.Left + x, rect.Top), true); + context.LineTo(new Point(rect.Right - x, rect.Top)); + context.ArcTo( + new Point(rect.Right, rect.Top + y), + arcSize, + rotationAngle: PiOver2, + isLargeArc: false, + SweepDirection.Clockwise); + + // Line 2 + Corner 2 + context.LineTo(new Point(rect.Right, rect.Bottom - y)); + context.ArcTo( + new Point(rect.Right - x, rect.Bottom), + arcSize, + rotationAngle: PiOver2, + isLargeArc: false, + SweepDirection.Clockwise); + + // Line 3 + Corner 3 + context.LineTo(new Point(rect.Left + x, rect.Bottom)); + context.ArcTo( + new Point(rect.Left, rect.Bottom - y), + arcSize, + rotationAngle: PiOver2, + isLargeArc: false, + SweepDirection.Clockwise); + + // Line 4 + Corner 4 + context.LineTo(new Point(rect.Left, rect.Top + y)); + context.ArcTo( + new Point(rect.Left + x, rect.Top), + arcSize, + rotationAngle: PiOver2, + isLargeArc: false, + SweepDirection.Clockwise); + + context.EndFigure(true); + } + + return geometry; + } } + /// protected override Size MeasureOverride(Size availableSize) { return new Size(StrokeThickness, StrokeThickness); diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index 574638c2bc..f2bd1947d6 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -42,6 +42,9 @@ namespace Avalonia.Controls /// /// A control that lets the user select from a range of values by moving a Thumb control along a Track. /// + [TemplatePart("PART_DecreaseButton", typeof(Button))] + [TemplatePart("PART_IncreaseButton", typeof(Button))] + [TemplatePart("PART_Track", typeof(Track))] [PseudoClasses(":vertical", ":horizontal", ":pressed")] public class Slider : RangeBase { diff --git a/src/Avalonia.Controls/SplitButton/SplitButton.cs b/src/Avalonia.Controls/SplitButton/SplitButton.cs index f1d07b2679..f2f4e951ae 100644 --- a/src/Avalonia.Controls/SplitButton/SplitButton.cs +++ b/src/Avalonia.Controls/SplitButton/SplitButton.cs @@ -1,7 +1,6 @@ using System; using System.Windows.Input; using Avalonia.Controls.Metadata; -using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; @@ -13,6 +12,8 @@ namespace Avalonia.Controls /// A button with primary and secondary parts that can each be pressed separately. /// The primary part behaves like a and the secondary part opens a flyout. /// + [TemplatePart("PART_PrimaryButton", typeof(Button))] + [TemplatePart("PART_SecondaryButton", typeof(Button))] [PseudoClasses(pcFlyoutOpen, pcPressed)] public class SplitButton : ContentControl, ICommandSource { @@ -314,16 +315,9 @@ namespace Avalonia.Controls } // Must unregister events here while a reference to the old flyout still exists - if (oldFlyout != null) - { - UnregisterFlyoutEvents(oldFlyout); - } - - if (newFlyout != null) - { - RegisterFlyoutEvents(newFlyout); - } + UnregisterFlyoutEvents(oldFlyout); + RegisterFlyoutEvents(newFlyout); UpdatePseudoClasses(); } @@ -415,6 +409,22 @@ namespace Avalonia.Controls } } + /// + /// Invoked when the split button's flyout is opened. + /// + protected virtual void OnFlyoutOpened() + { + // Available for derived types + } + + /// + /// Invoked when the split button's flyout is closed. + /// + protected virtual void OnFlyoutClosed() + { + // Available for derived types + } + //////////////////////////////////////////////////////////////////////// // Event Handling //////////////////////////////////////////////////////////////////////// @@ -464,6 +474,8 @@ namespace Avalonia.Controls { _isFlyoutOpen = true; UpdatePseudoClasses(); + + OnFlyoutOpened(); } } @@ -479,6 +491,8 @@ namespace Avalonia.Controls { _isFlyoutOpen = false; UpdatePseudoClasses(); + + OnFlyoutClosed(); } } } diff --git a/src/Avalonia.Controls/SplitView.cs b/src/Avalonia.Controls/SplitView.cs index d2161deb6e..ae1605a985 100644 --- a/src/Avalonia.Controls/SplitView.cs +++ b/src/Avalonia.Controls/SplitView.cs @@ -77,6 +77,7 @@ namespace Avalonia.Controls /// /// A control with two views: A collapsible pane and an area for content /// + [TemplatePart("PART_PaneRoot", typeof(Panel))] [PseudoClasses(":open", ":closed")] [PseudoClasses(":compactoverlay", ":compactinline", ":overlay", ":inline")] [PseudoClasses(":left", ":right")] diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index feb425a9c3..50c48d2bb0 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -123,7 +123,7 @@ namespace Avalonia.Controls index = Children.Count - 1; break; case NavigationDirection.Next: - if (index != -1) ++index; + ++index; break; case NavigationDirection.Previous: if (index != -1) --index; diff --git a/src/Avalonia.Controls/TabControl.cs b/src/Avalonia.Controls/TabControl.cs index 1fbe9cc4db..70fecc7ce1 100644 --- a/src/Avalonia.Controls/TabControl.cs +++ b/src/Avalonia.Controls/TabControl.cs @@ -11,12 +11,14 @@ using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.VisualTree; using Avalonia.Automation; +using Avalonia.Controls.Metadata; namespace Avalonia.Controls { /// /// A tab control that displays a tab strip along with the content of the selected tab. /// + [TemplatePart("PART_ItemsPresenter", typeof(ItemsPresenter))] public class TabControl : SelectingItemsControl, IContentPresenterHost { /// diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 69b55b7222..36e5f7236f 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -28,78 +28,69 @@ namespace Avalonia.Controls public static readonly StyledProperty PaddingProperty = Decorator.PaddingProperty.AddOwner(); - // TODO: Define these attached properties elsewhere (e.g. on a Text class) and AddOwner - // them into TextBlock. - /// /// Defines the property. /// - public static readonly AttachedProperty FontFamilyProperty = - AvaloniaProperty.RegisterAttached( - nameof(FontFamily), - defaultValue: FontFamily.Default, - inherits: true); + public static readonly StyledProperty FontFamilyProperty = + TextElement.FontFamilyProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontSizeProperty = - AvaloniaProperty.RegisterAttached( - nameof(FontSize), - defaultValue: 12, - inherits: true); + public static readonly StyledProperty FontSizeProperty = + TextElement.FontSizeProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontStyleProperty = - AvaloniaProperty.RegisterAttached( - nameof(FontStyle), - inherits: true); + public static readonly StyledProperty FontStyleProperty = + TextElement.FontStyleProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontWeightProperty = - AvaloniaProperty.RegisterAttached( - nameof(FontWeight), - inherits: true, - defaultValue: FontWeight.Normal); + public static readonly StyledProperty FontWeightProperty = + TextElement.FontWeightProperty.AddOwner(); /// - /// Defines the property. + /// Defines the property. /// - public static readonly AttachedProperty FontStretchProperty = - AvaloniaProperty.RegisterAttached( - nameof(FontStretch), - inherits: true, - defaultValue: FontStretch.Normal); - + public static readonly StyledProperty FontStretchProperty = + TextElement.FontStretchProperty.AddOwner(); + /// /// Defines the property. /// - public static readonly AttachedProperty ForegroundProperty = - AvaloniaProperty.RegisterAttached( - nameof(Foreground), - Brushes.Black, - inherits: true); + public static readonly StyledProperty ForegroundProperty = + TextElement.ForegroundProperty.AddOwner(); + + /// + /// DependencyProperty for property. + /// + public static readonly AttachedProperty BaselineOffsetProperty = + AvaloniaProperty.RegisterAttached( + nameof(BaselineOffset), + 0, + true); /// /// Defines the property. /// - public static readonly StyledProperty LineHeightProperty = - AvaloniaProperty.Register( + public static readonly AttachedProperty LineHeightProperty = + AvaloniaProperty.RegisterAttached( nameof(LineHeight), double.NaN, - validate: IsValidLineHeight); + validate: IsValidLineHeight, + inherits: true); /// /// Defines the property. /// - public static readonly StyledProperty MaxLinesProperty = - AvaloniaProperty.Register( + public static readonly AttachedProperty MaxLinesProperty = + AvaloniaProperty.RegisterAttached( nameof(MaxLines), - validate: IsValidMaxLines); + validate: IsValidMaxLines, + inherits: true); /// /// Defines the property. @@ -121,20 +112,24 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly StyledProperty TextAlignmentProperty = - AvaloniaProperty.Register(nameof(TextAlignment)); + public static readonly AttachedProperty TextAlignmentProperty = + AvaloniaProperty.RegisterAttached(nameof(TextAlignment), + inherits: true); /// /// Defines the property. /// - public static readonly StyledProperty TextWrappingProperty = - AvaloniaProperty.Register(nameof(TextWrapping)); + public static readonly AttachedProperty TextWrappingProperty = + AvaloniaProperty.RegisterAttached(nameof(TextWrapping), + inherits: true); /// /// Defines the property. /// - public static readonly StyledProperty TextTrimmingProperty = - AvaloniaProperty.Register(nameof(TextTrimming), defaultValue: TextTrimming.None); + public static readonly AttachedProperty TextTrimmingProperty = + AvaloniaProperty.RegisterAttached(nameof(TextTrimming), + defaultValue: TextTrimming.None, + inherits: true); /// /// Defines the property. @@ -222,7 +217,7 @@ namespace Avalonia.Controls public InlineCollection Inlines { get; } /// - /// Gets or sets the font family. + /// Gets or sets the font family used to draw the control's text. /// public FontFamily FontFamily { @@ -231,7 +226,7 @@ namespace Avalonia.Controls } /// - /// Gets or sets the font size. + /// Gets or sets the size of the control's text in points. /// public double FontSize { @@ -240,7 +235,7 @@ namespace Avalonia.Controls } /// - /// Gets or sets the font style. + /// Gets or sets the font style used to draw the control's text. /// public FontStyle FontStyle { @@ -249,16 +244,16 @@ namespace Avalonia.Controls } /// - /// Gets or sets the font weight. + /// Gets or sets the font weight used to draw the control's text. /// public FontWeight FontWeight { get { return GetValue(FontWeightProperty); } set { SetValue(FontWeightProperty, value); } } - + /// - /// Gets or sets the font stretch. + /// Gets or sets the font stretch used to draw the control's text. /// public FontStretch FontStretch { @@ -267,7 +262,7 @@ namespace Avalonia.Controls } /// - /// Gets or sets a brush used to paint the text. + /// Gets or sets the brush used to draw the control's text and other foreground elements. /// public IBrush? Foreground { @@ -328,127 +323,193 @@ namespace Avalonia.Controls get => GetValue(TextDecorationsProperty); set => SetValue(TextDecorationsProperty, value); } + + protected override bool BypassFlowDirectionPolicies => true; /// - /// Gets the value of the attached on a control. + /// The BaselineOffset property provides an adjustment to baseline offset /// - /// The control. - /// The font family. - public static FontFamily GetFontFamily(Control control) + public double BaselineOffset { - return control.GetValue(FontFamilyProperty); + get { return (double)GetValue(BaselineOffsetProperty); } + set { SetValue(BaselineOffsetProperty, value); } } /// - /// Gets the value of the attached on a control. + /// Reads the attached property from the given element /// - /// The control. - /// The font size. - public static double GetFontSize(Control control) + /// The element to which to read the attached property. + public static double GetBaselineOffset(Control control) { - return control.GetValue(FontSizeProperty); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + return control.GetValue(BaselineOffsetProperty); } /// - /// Gets the value of the attached on a control. + /// Writes the attached property BaselineOffset to the given element. /// - /// The control. - /// The font style. - public static FontStyle GetFontStyle(Control control) + /// The element to which to write the attached property. + /// The property value to set + public static void SetBaselineOffset(Control control, double value) { - return control.GetValue(FontStyleProperty); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + control.SetValue(BaselineOffsetProperty, value); } /// - /// Gets the value of the attached on a control. + /// Reads the attached property from the given element /// - /// The control. - /// The font weight. - public static FontWeight GetFontWeight(Control control) + /// The element to which to read the attached property. + public static TextAlignment GetTextAlignment(Control control) { - return control.GetValue(FontWeightProperty); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + return control.GetValue(TextAlignmentProperty); } - + /// - /// Gets the value of the attached on a control. + /// Writes the attached property BaselineOffset to the given element. /// - /// The control. - /// The font stretch. - public static FontStretch GetFontStretch(Control control) + /// The element to which to write the attached property. + /// The property value to set + public static void SetTextAlignment(Control control, TextAlignment alignment) { - return control.GetValue(FontStretchProperty); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + control.SetValue(TextAlignmentProperty, alignment); } /// - /// Gets the value of the attached on a control. + /// Reads the attached property from the given element /// - /// The control. - /// The foreground. - public static IBrush? GetForeground(Control control) + /// The element to which to read the attached property. + public static TextWrapping GetTextWrapping(Control control) { - return control.GetValue(ForegroundProperty); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + return control.GetValue(TextWrappingProperty); } /// - /// Sets the value of the attached on a control. + /// Writes the attached property BaselineOffset to the given element. /// - /// The control. - /// The property value to set. - public static void SetFontFamily(Control control, FontFamily value) + /// The element to which to write the attached property. + /// The property value to set + public static void SetTextWrapping(Control control, TextWrapping wrapping) { - control.SetValue(FontFamilyProperty, value); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + control.SetValue(TextWrappingProperty, wrapping); } /// - /// Sets the value of the attached on a control. + /// Reads the attached property from the given element /// - /// The control. - /// The property value to set. - public static void SetFontSize(Control control, double value) + /// The element to which to read the attached property. + public static TextTrimming GetTextTrimming(Control control) { - control.SetValue(FontSizeProperty, value); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + return control.GetValue(TextTrimmingProperty); } /// - /// Sets the value of the attached on a control. + /// Writes the attached property BaselineOffset to the given element. /// - /// The control. - /// The property value to set. - public static void SetFontStyle(Control control, FontStyle value) + /// The element to which to write the attached property. + /// The property value to set + public static void SetTextTrimming(Control control, TextTrimming trimming) { - control.SetValue(FontStyleProperty, value); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + control.SetValue(TextTrimmingProperty, trimming); } /// - /// Sets the value of the attached on a control. + /// Reads the attached property from the given element /// - /// The control. - /// The property value to set. - public static void SetFontWeight(Control control, FontWeight value) + /// The element to which to read the attached property. + public static double GetLineHeight(Control control) { - control.SetValue(FontWeightProperty, value); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + return control.GetValue(LineHeightProperty); } - + /// - /// Sets the value of the attached on a control. + /// Writes the attached property BaselineOffset to the given element. /// - /// The control. - /// The property value to set. - public static void SetFontStretch(Control control, FontStretch value) + /// The element to which to write the attached property. + /// The property value to set + public static void SetLineHeight(Control control, double height) { - control.SetValue(FontStretchProperty, value); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + control.SetValue(LineHeightProperty, height); } /// - /// Sets the value of the attached on a control. + /// Reads the attached property from the given element /// - /// The control. - /// The property value to set. - public static void SetForeground(Control control, IBrush? value) + /// The element to which to read the attached property. + public static int GetMaxLines(Control control) { - control.SetValue(ForegroundProperty, value); + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + return control.GetValue(MaxLinesProperty); } + /// + /// Writes the attached property BaselineOffset to the given element. + /// + /// The element to which to write the attached property. + /// The property value to set + public static void SetMaxLines(Control control, int maxLines) + { + if (control == null) + { + throw new ArgumentNullException(nameof(control)); + } + + control.SetValue(MaxLinesProperty, maxLines); + } + + /// /// Renders the to a drawing context. /// @@ -562,7 +623,7 @@ namespace Avalonia.Controls return finalSize; } - _constraint = finalSize; + _constraint = new Size(finalSize.Width, Math.Ceiling(finalSize.Height)); _textLayout = null; @@ -588,15 +649,19 @@ namespace Avalonia.Controls case nameof (FontWeight): case nameof (FontStyle): case nameof (FontFamily): + case nameof (FontStretch): case nameof (TextWrapping): case nameof (TextTrimming): case nameof (TextAlignment): + case nameof (FlowDirection): case nameof (Padding): case nameof (LineHeight): case nameof (MaxLines): + + case nameof (InlinesProperty): case nameof (Text): case nameof (TextDecorations): diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 4d71717776..947830b217 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -23,6 +23,7 @@ namespace Avalonia.Controls /// /// Represents a control that can be used to display or edit unformatted text. /// + [TemplatePart("PART_TextPresenter", typeof(TextPresenter))] [PseudoClasses(":empty")] public class TextBox : TemplatedControl, UndoRedoHelper.IUndoRedoHost { @@ -77,6 +78,9 @@ namespace Avalonia.Controls public static readonly StyledProperty MaxLengthProperty = AvaloniaProperty.Register(nameof(MaxLength), defaultValue: 0); + public static readonly StyledProperty MaxLinesProperty = + AvaloniaProperty.Register(nameof(MaxLines), defaultValue: 0); + public static readonly DirectProperty TextProperty = TextBlock.TextProperty.AddOwnerWithDataValidation( o => o.Text, @@ -349,6 +353,12 @@ namespace Avalonia.Controls set { SetValue(MaxLengthProperty, value); } } + public int MaxLines + { + get { return GetValue(MaxLinesProperty); } + set { SetValue(MaxLinesProperty, value); } + } + [Content] public string? Text { @@ -670,6 +680,39 @@ namespace Avalonia.Controls _selectedTextChangesMadeSinceLastUndoSnapshot++; SnapshotUndoRedo(ignoreChangeCount: false); + if (_presenter != null && MaxLines > 0) + { + var lineCount = _presenter.TextLayout.TextLines.Count; + + var length = 0; + + var graphemeEnumerator = new GraphemeEnumerator(input.AsMemory()); + + while (graphemeEnumerator.MoveNext()) + { + var grapheme = graphemeEnumerator.Current; + + if (grapheme.FirstCodepoint.IsBreakChar) + { + if(lineCount + 1 > MaxLines) + { + break; + } + else + { + lineCount++; + } + } + + length += grapheme.Text.Length; + } + + if (length < input.Length) + { + input = input.Remove(Math.Max(0, length)); + } + } + var text = Text ?? string.Empty; var newLength = input.Length + text.Length - Math.Abs(SelectionStart - SelectionEnd); @@ -997,18 +1040,25 @@ namespace Avalonia.Controls SetSelectionForControlBackspace(); } - if (!DeleteSelection() && caretIndex > 0) + if (!DeleteSelection()) { - _presenter.MoveCaretHorizontal(LogicalDirection.Backward); + var characterHit = _presenter.GetNextCharacterHit(LogicalDirection.Backward); - var removedCharacters = Math.Max(0, caretIndex - _presenter.CaretIndex); - - var length = Math.Max(0, caretIndex - removedCharacters); + var backspacePosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - SetTextInternal(text.Substring(0, length) + - text.Substring(caretIndex)); + if (caretIndex != backspacePosition) + { + var start = Math.Min(backspacePosition, caretIndex); + var end = Math.Max(backspacePosition, caretIndex); - CaretIndex = _presenter.CaretIndex; + var length = end - start; + + var editedText = text.Substring(0, start) + text.Substring(Math.Min(end, text.Length)); + + SetTextInternal(editedText); + + CaretIndex = start; + } } SnapshotUndoRedo(); @@ -1024,15 +1074,21 @@ namespace Avalonia.Controls SetSelectionForControlDelete(); } - if (!DeleteSelection() && caretIndex < text.Length) + if (!DeleteSelection()) { var characterHit = _presenter.GetNextCharacterHit(); - var removedCharacters = Math.Max(0, - characterHit.FirstCharacterIndex + characterHit.TrailingLength - caretIndex); + var nextPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength; + + if(nextPosition != caretIndex) + { + var start = Math.Min(nextPosition, caretIndex); + var end = Math.Max(nextPosition, caretIndex); - SetTextInternal(text.Substring(0, caretIndex) + - text.Substring(caretIndex + removedCharacters)); + var editedText = text.Substring(0, start) + text.Substring(Math.Min(end, text.Length)); + + SetTextInternal(editedText); + } } SnapshotUndoRedo(); @@ -1249,17 +1305,17 @@ namespace Avalonia.Controls private void MoveHorizontal(int direction, bool wholeWord, bool isSelecting) { + if (_presenter == null) + { + return; + } + var text = Text ?? string.Empty; var selectionStart = SelectionStart; var selectionEnd = SelectionEnd; if (!wholeWord) { - if (_presenter == null) - { - return; - } - if (isSelecting) { _presenter.MoveCaretToTextPosition(selectionEnd); @@ -1303,10 +1359,16 @@ namespace Avalonia.Controls SelectionEnd += offset; + _presenter.MoveCaretToTextPosition(SelectionEnd); + if (!isSelecting) { CaretIndex = SelectionEnd; } + else + { + SelectionStart = selectionStart; + } } } @@ -1326,10 +1388,10 @@ namespace Avalonia.Controls else { var textLines = _presenter.TextLayout.TextLines; - var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(caretIndex, true); + var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(caretIndex, false); var textLine = textLines[lineIndex]; - _presenter.MoveCaretToTextPosition(textLine.TextRange.Start); + _presenter.MoveCaretToTextPosition(textLine.FirstTextSourceIndex); } } @@ -1350,16 +1412,10 @@ namespace Avalonia.Controls else { var textLines = _presenter.TextLayout.TextLines; - var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(caretIndex, true); + var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(caretIndex, false); var textLine = textLines[lineIndex]; - - if (caretIndex == textLine.TextRange.Start + textLine.TextRange.Length - textLine.NewLineLength && - lineIndex + 1 < textLines.Count) - { - textLine = textLines[++lineIndex]; - } - var textPosition = textLine.TextRange.Start + textLine.TextRange.Length - textLine.NewLineLength; + var textPosition = textLine.FirstTextSourceIndex + textLine.Length; _presenter.MoveCaretToTextPosition(textPosition, true); } diff --git a/src/Avalonia.Controls/ToggleSwitch.cs b/src/Avalonia.Controls/ToggleSwitch.cs index f33f2b9df3..fd6c202c6f 100644 --- a/src/Avalonia.Controls/ToggleSwitch.cs +++ b/src/Avalonia.Controls/ToggleSwitch.cs @@ -9,6 +9,8 @@ namespace Avalonia.Controls /// /// A Toggle Switch control. /// + [TemplatePart("MovingKnobs", typeof(Panel))] + [TemplatePart("SwitchKnob", typeof(Panel))] [PseudoClasses(":dragging")] public class ToggleSwitch : ToggleButton { diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index a4fe154515..75a34659a2 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -1,5 +1,6 @@ using System; using System.Reactive.Linq; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Platform; using Avalonia.Controls.Primitives; using Avalonia.Input; @@ -14,7 +15,6 @@ using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.Utilities; using Avalonia.VisualTree; -using JetBrains.Annotations; namespace Avalonia.Controls { @@ -26,6 +26,7 @@ namespace Avalonia.Controls /// It handles scheduling layout, styling and rendering as well as /// tracking the widget's . /// + [TemplatePart("PART_TransparencyFallback", typeof(Border))] public abstract class TopLevel : ContentControl, IInputRoot, ILayoutRoot, @@ -85,6 +86,8 @@ namespace Avalonia.Controls private readonly IKeyboardNavigationHandler? _keyboardNavigationHandler; private readonly IPlatformRenderInterface? _renderInterface; private readonly IGlobalStyles? _globalStyles; + private readonly PointerOverPreProcessor? _pointerOverPreProcessor; + private readonly IDisposable? _pointerOverPreProcessorSubscription; private Size _clientSize; private Size? _frameSize; private WindowTransparencyLevel _actualTransparencyLevel; @@ -193,6 +196,9 @@ namespace Avalonia.Controls } impl.LostFocus += PlatformImpl_LostFocus; + + _pointerOverPreProcessor = new PointerOverPreProcessor(this); + _pointerOverPreProcessorSubscription = _inputManager?.PreProcess.Subscribe(_pointerOverPreProcessor); } /// @@ -281,9 +287,7 @@ namespace Avalonia.Controls /// IKeyboardNavigationHandler IInputRoot.KeyboardNavigationHandler => _keyboardNavigationHandler!; - /// - /// Gets or sets the input element that the pointer is currently over. - /// + /// IInputElement? IInputRoot.PointerOverElement { get { return GetValue(PointerOverElementProperty); } @@ -348,6 +352,12 @@ namespace Avalonia.Controls /// protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager(this); + public override void InvalidateMirrorTransform() + { + } + + protected override bool BypassFlowDirectionPolicies => true; + /// /// Handles a paint notification from . /// @@ -370,10 +380,12 @@ namespace Avalonia.Controls Renderer?.Dispose(); Renderer = null!; - - (this as IInputRoot).MouseDevice?.TopLevelClosed(this); + + _pointerOverPreProcessor?.OnCompleted(); + _pointerOverPreProcessorSubscription?.Dispose(); + PlatformImpl = null; - + var logicalArgs = new LogicalTreeAttachmentEventArgs(this, this, null); ((ILogical)this).NotifyDetachedFromLogicalTree(logicalArgs); @@ -507,12 +519,17 @@ namespace Avalonia.Controls /// The event args. private void HandleInput(RawInputEventArgs e) { + if (e is RawPointerEventArgs pointerArgs) + { + pointerArgs.InputHitTestResult = this.InputHitTest(pointerArgs.Position); + } + _inputManager?.ProcessInput(e); } private void SceneInvalidated(object? sender, SceneInvalidatedEventArgs e) { - (this as IInputRoot).MouseDevice?.SceneInvalidated(this, e.DirtyRect); + _pointerOverPreProcessor?.SceneInvalidated(e.DirtyRect); } void PlatformImpl_LostFocus() diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 9a276e74d2..1d806913dd 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -849,7 +849,7 @@ namespace Avalonia.Controls /// The desired items. private static void SynchronizeItems(IList items, IEnumerable desired) { - var list = items.Cast().ToList(); + var list = items.Cast(); var toRemove = list.Except(desired).ToList(); var toAdd = desired.Except(list).ToList(); diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index 20c0ed386d..a0a3c09942 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -12,6 +12,7 @@ namespace Avalonia.Controls /// /// An item in a . /// + [TemplatePart("PART_Header", typeof(IControl))] [PseudoClasses(":pressed", ":selected")] public class TreeViewItem : HeaderedItemsControl, ISelectable { diff --git a/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs b/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs index 0b1c4fc90e..9d13daa453 100644 --- a/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs +++ b/src/Avalonia.Controls/Utils/CollectionChangedEventManager.cs @@ -105,7 +105,7 @@ namespace Avalonia.Controls.Utils static void Notify( INotifyCollectionChanged incc, NotifyCollectionChangedEventArgs args, - List> listeners) + WeakReference[] listeners) { foreach (var l in listeners) { @@ -132,7 +132,7 @@ namespace Avalonia.Controls.Utils } } - var l = Listeners.ToList(); + var l = Listeners.ToArray(); if (Dispatcher.UIThread.CheckAccess()) { diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index feacc3e63a..993b3aaa1b 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -12,7 +12,6 @@ using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Styling; -using JetBrains.Annotations; namespace Avalonia.Controls { @@ -258,7 +257,7 @@ namespace Avalonia.Controls /// /// Gets a collection of child windows owned by this window. /// - public IReadOnlyList OwnedWindows => _children.Select(x => x.child).ToList(); + public IReadOnlyList OwnedWindows => _children.Select(x => x.child).ToArray(); /// /// Gets or sets a value indicating how the window will size itself to fit its content. @@ -527,7 +526,7 @@ namespace Avalonia.Controls private void CloseInternal() { - foreach (var (child, _) in _children.ToList()) + foreach (var (child, _) in _children.ToArray()) { child.CloseInternal(); } @@ -551,7 +550,7 @@ namespace Avalonia.Controls bool canClose = true; - foreach (var (child, _) in _children.ToList()) + foreach (var (child, _) in _children.ToArray()) { if (child.ShouldCancelClose(args)) { diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index f8a7cdc690..0270000d8c 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -16,14 +16,8 @@ - - - - - - diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 6b1934ed06..f100be5d5b 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Viewport; +using Avalonia.Utilities; using InputProtocol = Avalonia.Remote.Protocol.Input; namespace Avalonia.DesignerSupport.Remote.HtmlTransport @@ -320,15 +321,13 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport ? null : modifiersText .Split(',') - .Select(x => (InputProtocol.InputModifiers)Enum.Parse( - typeof(InputProtocol.InputModifiers), x, true)) + .Select(x => EnumHelper.Parse(x, true)) .ToArray(); private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => string.IsNullOrWhiteSpace(buttonText) ? InputProtocol.MouseButton.None - : (InputProtocol.MouseButton)Enum.Parse( - typeof(InputProtocol.MouseButton), buttonText, true); + : EnumHelper.Parse(buttonText, true); private static double ParseDouble(string text) => double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture); diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json index 6a5fea5b10..403bb5a59a 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json @@ -377,9 +377,9 @@ "dev": true }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "requires": { "lodash": "^4.17.14" @@ -1078,12 +1078,6 @@ "json5": "^2.1.2" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -1636,9 +1630,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "mobx": { @@ -1839,12 +1833,6 @@ "ms": "^2.1.1" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -2592,12 +2580,6 @@ "json5": "^2.1.2" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index 1fc3604f70..2fb7c07b6f 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -16,14 +16,8 @@ - - - - - - @@ -34,4 +28,5 @@ + diff --git a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs index 683c2e6549..9029ddf2bd 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs @@ -105,13 +105,15 @@ namespace Avalonia.Diagnostics private static IDisposable Open(Application? application, DevToolsOptions options, Window? owner = default) { + var focussedControl = KeyboardDevice.Instance?.FocusedElement as IControl; if (application is null) { throw new ArgumentNullException(nameof(application)); } if (s_open.TryGetValue(application, out var window)) - { + { window.Activate(); + window.SelectedControl(focussedControl); } else { @@ -122,7 +124,7 @@ namespace Avalonia.Diagnostics Height = options.Size.Height, }; window.SetOptions(options); - + window.SelectedControl(focussedControl); window.Closed += DevToolsClosed; s_open.Add(application, window); if (options.ShowAsChildWindow && owner is { }) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index a1fd8ed028..a1fd425571 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -18,7 +18,7 @@ namespace Avalonia.Diagnostics.ViewModels internal class ControlDetailsViewModel : ViewModelBase, IDisposable { private readonly IAvaloniaObject _avaloniaObject; - private IDictionary>? _propertyIndex; + private IDictionary? _propertyIndex; private PropertyViewModel? _selectedProperty; private DataGridCollectionView? _propertiesView; private bool _snapshotStyles; @@ -472,9 +472,9 @@ namespace Avalonia.Diagnostics.ViewModels .Concat(GetClrProperties(o, _showImplementedInterfaces)) .OrderBy(x => x, PropertyComparer.Instance) .ThenBy(x => x.Name) - .ToList(); + .ToArray(); - _propertyIndex = properties.GroupBy(x => x.Key).ToDictionary(x => x.Key, x => x.ToList()); + _propertyIndex = properties.GroupBy(x => x.Key).ToDictionary(x => x.Key, x => x.ToArray()); var view = new DataGridCollectionView(properties); view.GroupDescriptions.Add(new DataGridPathGroupDescription(nameof(AvaloniaPropertyViewModel.Group))); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index e08c5bc8dd..9e8a5d8d9b 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -7,7 +7,6 @@ using Avalonia.Diagnostics.Models; using Avalonia.Input; using Avalonia.Metadata; using Avalonia.Threading; -using System.Reactive.Linq; using System.Linq; namespace Avalonia.Diagnostics.ViewModels @@ -59,8 +58,8 @@ namespace Avalonia.Diagnostics.ViewModels .Subscribe(e => { PointerOverRoot = e.Root; - PointerOverElement = e.Root.GetInputElementsAt(e.Position).FirstOrDefault(); - }); + PointerOverElement = e.Root.InputHitTest(e.Position); + }); #nullable restore } Console = new ConsoleViewModel(UpdateConsoleContext); @@ -163,8 +162,7 @@ namespace Avalonia.Diagnostics.ViewModels } catch { } }, - TimeSpan.FromMilliseconds(0), - DispatcherPriority.ApplicationIdle); + TimeSpan.FromMilliseconds(0)); } RaiseAndSetIfChanged(ref _content, value); diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml b/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml index 264a0de359..7dec878e69 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/ConsoleView.xaml @@ -11,7 +11,7 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> - > + - > + diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml b/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml index db48aaaa8e..c404a5c382 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/LayoutExplorerView.axaml @@ -47,7 +47,7 @@ diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs index 1e152dcdd4..3b143d3b58 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs @@ -110,7 +110,7 @@ namespace Avalonia.Diagnostics.Views { #pragma warning disable CS0618 // Type or member is obsolete var point = (topLevel as IInputRoot)?.MouseDevice?.GetPosition(topLevel) ?? default; -#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore CS0618 // Type or member is obsolete return (IControl?)topLevel.GetVisualsAt(point, x => { @@ -162,13 +162,18 @@ namespace Avalonia.Diagnostics.Views return; } - var root = Root as TopLevel - ?? vm.PointerOverRoot as TopLevel; + var root = vm.PointerOverRoot as TopLevel; + if (root is null) { return; } + if (root is PopupRoot pr && pr.ParentTopLevel != null) + { + root = pr.ParentTopLevel; + } + switch (e.Modifiers) { case RawInputModifiers.Control when (e.Key == Key.LeftShift || e.Key == Key.RightShift): @@ -255,5 +260,13 @@ namespace Avalonia.Diagnostics.Views } } } + + internal void SelectedControl(IControl? control) + { + if (control is { }) + { + (DataContext as MainViewModel)?.SelectControl(control); + } + } } } diff --git a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj index 770d15ea27..a311efdfb0 100644 --- a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj +++ b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj @@ -15,4 +15,5 @@ + diff --git a/src/Avalonia.Dialogs/ManagedFileChooser.cs b/src/Avalonia.Dialogs/ManagedFileChooser.cs index 9058c405a3..199a4d6620 100644 --- a/src/Avalonia.Dialogs/ManagedFileChooser.cs +++ b/src/Avalonia.Dialogs/ManagedFileChooser.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Interactivity; @@ -9,6 +10,8 @@ using Avalonia.LogicalTree; namespace Avalonia.Dialogs { + [TemplatePart("QuickLinks", typeof(Control))] + [TemplatePart("Files", typeof(ListBox))] public class ManagedFileChooser : TemplatedControl { private Control _quickLinksRoot; diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index 90221bb922..addc248d58 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -7,7 +7,7 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Headless { diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs index f22fb4f317..083b16c107 100644 --- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -133,9 +133,12 @@ namespace Avalonia.Headless class HeadlessTextShaperStub : ITextShaperImpl { - public ShapedBuffer ShapeText(ReadOnlySlice text, GlyphTypeface typeface, double fontRenderingEmSize, - CultureInfo culture, sbyte bidiLevel) + public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) { + var typeface = options.Typeface; + var fontRenderingEmSize = options.FontRenderingEmSize; + var bidiLevel = options.BidLevel; + return new ShapedBuffer(text, text.Length, typeface, fontRenderingEmSize, bidiLevel); } } diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt deleted file mode 100644 index 68b4d4754f..0000000000 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ /dev/null @@ -1,29 +0,0 @@ -Compat issues with assembly Avalonia.Input: -MembersMustExist : Member 'public Avalonia.Platform.IPlatformHandle Avalonia.Input.Cursor.PlatformCursor.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.RightTappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.TappedEvent' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.IFocusManager.RemoveFocusScope(Avalonia.Input.IFocusScope)' is present in the implementation but not in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.TextInputOptionsQueryEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.TappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_TextInputOptionsQuery(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_TextInputOptionsQuery(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetClient(Avalonia.Input.TextInput.ITextInputMethodClient)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptions)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs)' does not exist in the implementation but it does exist in the contract. -EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Email' is (System.Int32)5 in the implementation but (System.Int32)1 in the contract. -EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Number' is (System.Int32)4 in the implementation but (System.Int32)3 in the contract. -EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Password' is (System.Int32)8 in the implementation but (System.Int32)5 in the contract. -MembersMustExist : Member 'public Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Phone' does not exist in the implementation but it does exist in the contract. -EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Url' is (System.Int32)6 in the implementation but (System.Int32)4 in the contract. -TypesMustExist : Type 'Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Platform.IStandardCursorFactory' does not exist in the implementation but it does exist in the contract. -Total Issues: 27 diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj deleted file mode 100644 index e66d1d7e7c..0000000000 --- a/src/Avalonia.Input/Avalonia.Input.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - - - - - - diff --git a/src/Avalonia.Input/FocusManager.cs b/src/Avalonia.Input/FocusManager.cs deleted file mode 100644 index 30301788bc..0000000000 --- a/src/Avalonia.Input/FocusManager.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using Avalonia.Interactivity; -using Avalonia.VisualTree; - -namespace Avalonia.Input -{ - /// - /// Manages focus for the application. - /// - public class FocusManager : IFocusManager - { - /// - /// The focus scopes in which the focus is currently defined. - /// - private readonly ConditionalWeakTable _focusScopes = - new ConditionalWeakTable(); - - /// - /// Initializes a new instance of the class. - /// - static FocusManager() - { - InputElement.PointerPressedEvent.AddClassHandler( - typeof(IInputElement), - new EventHandler(OnPreviewPointerPressed), - RoutingStrategies.Tunnel); - } - - /// - /// Gets the instance of the . - /// - public static IFocusManager? Instance => AvaloniaLocator.Current.GetService(); - - /// - /// Gets the currently focused . - /// - public IInputElement? Current => KeyboardDevice.Instance?.FocusedElement; - - /// - /// Gets the current focus scope. - /// - public IFocusScope? Scope - { - get; - private set; - } - - /// - /// Focuses a control. - /// - /// The control to focus. - /// The method by which focus was changed. - /// Any key modifiers active at the time of focus. - public void Focus( - IInputElement? control, - NavigationMethod method = NavigationMethod.Unspecified, - KeyModifiers keyModifiers = KeyModifiers.None) - { - if (control != null) - { - var scope = GetFocusScopeAncestors(control) - .FirstOrDefault(); - - if (scope != null) - { - Scope = scope; - SetFocusedElement(scope, control, method, keyModifiers); - } - } - else if (Current != null) - { - // If control is null, set focus to the topmost focus scope. - foreach (var scope in GetFocusScopeAncestors(Current).Reverse().ToList()) - { - if (scope != Scope && - _focusScopes.TryGetValue(scope, out var element) && - element != null) - { - Focus(element, method); - return; - } - } - - if (Scope is object) - { - // Couldn't find a focus scope, clear focus. - SetFocusedElement(Scope, null); - } - } - } - - public IInputElement? GetFocusedElement(IInputElement e) - { - if (e is IFocusScope scope) - { - _focusScopes.TryGetValue(scope, out var result); - return result; - } - - return null; - } - - /// - /// Sets the currently focused element in the specified scope. - /// - /// The focus scope. - /// The element to focus. May be null. - /// The method by which focus was changed. - /// Any key modifiers active at the time of focus. - /// - /// If the specified scope is the current then the keyboard focus - /// will change. - /// - public void SetFocusedElement( - IFocusScope scope, - IInputElement? element, - NavigationMethod method = NavigationMethod.Unspecified, - KeyModifiers keyModifiers = KeyModifiers.None) - { - scope = scope ?? throw new ArgumentNullException(nameof(scope)); - - if (_focusScopes.TryGetValue(scope, out var existingElement)) - { - if (element != existingElement) - { - _focusScopes.Remove(scope); - _focusScopes.Add(scope, element); - } - } - else - { - _focusScopes.Add(scope, element); - } - - if (Scope == scope) - { - KeyboardDevice.Instance?.SetFocusedElement(element, method, keyModifiers); - } - } - - /// - /// Notifies the focus manager of a change in focus scope. - /// - /// The new focus scope. - public void SetFocusScope(IFocusScope scope) - { - scope = scope ?? throw new ArgumentNullException(nameof(scope)); - - if (!_focusScopes.TryGetValue(scope, out var e)) - { - // TODO: Make this do something useful, i.e. select the first focusable - // control, select a control that the user has specified to have default - // focus etc. - e = scope as IInputElement; - _focusScopes.Add(scope, e); - } - - Scope = scope; - Focus(e); - } - - public void RemoveFocusScope(IFocusScope scope) - { - scope = scope ?? throw new ArgumentNullException(nameof(scope)); - - if (_focusScopes.TryGetValue(scope, out _)) - { - SetFocusedElement(scope, null); - _focusScopes.Remove(scope); - } - - if (Scope == scope) - { - Scope = null; - } - } - - public static bool GetIsFocusScope(IInputElement e) => e is IFocusScope; - - /// - /// Checks if the specified element can be focused. - /// - /// The element. - /// True if the element can be focused. - private static bool CanFocus(IInputElement e) => e.Focusable && e.IsEffectivelyEnabled && e.IsVisible; - - /// - /// Gets the focus scope ancestors of the specified control, traversing popups. - /// - /// The control. - /// The focus scopes. - private static IEnumerable GetFocusScopeAncestors(IInputElement control) - { - IInputElement? c = control; - - while (c != null) - { - var scope = c as IFocusScope; - - if (scope != null && c.VisualRoot?.IsVisible == true) - { - yield return scope; - } - - c = c.GetVisualParent() ?? - ((c as IHostedVisualTreeRoot)?.Host as IInputElement); - } - } - - /// - /// Global handler for pointer pressed events. - /// - /// The event sender. - /// The event args. - private static void OnPreviewPointerPressed(object? sender, RoutedEventArgs e) - { - if (sender is null) - return; - - var ev = (PointerPressedEventArgs)e; - var visual = (IVisual)sender; - - if (sender == e.Source && ev.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) - { - IVisual? element = ev.Pointer?.Captured ?? e.Source as IInputElement; - - while (element != null) - { - if (element is IInputElement inputElement && CanFocus(inputElement)) - { - Instance?.Focus(inputElement, NavigationMethod.Pointer, ev.KeyModifiers); - - break; - } - - element = element.VisualParent; - } - } - } - } -} diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Input/Gestures.cs deleted file mode 100644 index 86e6e96a71..0000000000 --- a/src/Avalonia.Input/Gestures.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using Avalonia.Interactivity; -using Avalonia.VisualTree; - -namespace Avalonia.Input -{ - public static class Gestures - { - private static bool s_isDoubleTapped = false; - public static readonly RoutedEvent TappedEvent = RoutedEvent.Register( - "Tapped", - RoutingStrategies.Bubble, - typeof(Gestures)); - - public static readonly RoutedEvent DoubleTappedEvent = RoutedEvent.Register( - "DoubleTapped", - RoutingStrategies.Bubble, - typeof(Gestures)); - - public static readonly RoutedEvent RightTappedEvent = RoutedEvent.Register( - "RightTapped", - RoutingStrategies.Bubble, - typeof(Gestures)); - - public static readonly RoutedEvent ScrollGestureEvent = - RoutedEvent.Register( - "ScrollGesture", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent ScrollGestureEndedEvent = - RoutedEvent.Register( - "ScrollGestureEnded", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent PointerTouchPadGestureMagnifyEvent = - RoutedEvent.Register( - "PointerMagnifyGesture", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent PointerTouchPadGestureRotateEvent = - RoutedEvent.Register( - "PointerRotateGesture", RoutingStrategies.Bubble, typeof(Gestures)); - - public static readonly RoutedEvent PointerTouchPadGestureSwipeEvent = - RoutedEvent.Register( - "PointerSwipeGesture", RoutingStrategies.Bubble, typeof(Gestures)); - -#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. - private static readonly WeakReference s_lastPress = new WeakReference(null); -#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. - - static Gestures() - { - InputElement.PointerPressedEvent.RouteFinished.Subscribe(PointerPressed); - InputElement.PointerReleasedEvent.RouteFinished.Subscribe(PointerReleased); - } - - public static void AddTappedHandler(IInteractive element, EventHandler handler) - { - element.AddHandler(TappedEvent, handler); - } - - public static void AddDoubleTappedHandler(IInteractive element, EventHandler handler) - { - element.AddHandler(DoubleTappedEvent, handler); - } - - public static void AddRightTappedHandler(IInteractive element, EventHandler handler) - { - element.AddHandler(RightTappedEvent, handler); - } - - public static void RemoveTappedHandler(IInteractive element, EventHandler handler) - { - element.RemoveHandler(TappedEvent, handler); - } - - public static void RemoveDoubleTappedHandler(IInteractive element, EventHandler handler) - { - element.RemoveHandler(DoubleTappedEvent, handler); - } - - public static void RemoveRightTappedHandler(IInteractive element, EventHandler handler) - { - element.RemoveHandler(RightTappedEvent, handler); - } - - private static void PointerPressed(RoutedEventArgs ev) - { - if (ev.Source is null) - { - return; - } - - if (ev.Route == RoutingStrategies.Bubble) - { - var e = (PointerPressedEventArgs)ev; - var visual = (IVisual)ev.Source; - - if (e.ClickCount <= 1) - { - s_isDoubleTapped = false; - s_lastPress.SetTarget(ev.Source); - } - else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) - { - if (s_lastPress.TryGetTarget(out var target) && target == e.Source) - { - s_isDoubleTapped = true; - e.Source.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e)); - } - } - else - { - s_isDoubleTapped = false; - } - } - } - - private static void PointerReleased(RoutedEventArgs ev) - { - if (ev.Route == RoutingStrategies.Bubble) - { - var e = (PointerReleasedEventArgs)ev; - - if (s_lastPress.TryGetTarget(out var target) && target == e.Source) - { - if (e.InitialPressMouseButton == MouseButton.Left || e.InitialPressMouseButton == MouseButton.Right) - { - if (e.InitialPressMouseButton == MouseButton.Right) - { - e.Source.RaiseEvent(new TappedEventArgs(RightTappedEvent, e)); - } - //s_isDoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called. - //This behaviour matches UWP behaviour. - else if (s_isDoubleTapped == false) - { - e.Source.RaiseEvent(new TappedEventArgs(TappedEvent, e)); - } - } - } - } - } - } -} diff --git a/src/Avalonia.Input/IInputRoot.cs b/src/Avalonia.Input/IInputRoot.cs deleted file mode 100644 index 3e2b8cc477..0000000000 --- a/src/Avalonia.Input/IInputRoot.cs +++ /dev/null @@ -1,36 +0,0 @@ -using JetBrains.Annotations; - -namespace Avalonia.Input -{ - /// - /// Defines the interface for top-level input elements. - /// - public interface IInputRoot : IInputElement - { - /// - /// Gets or sets the access key handler. - /// - IAccessKeyHandler AccessKeyHandler { get; } - - /// - /// Gets or sets the keyboard navigation handler. - /// - IKeyboardNavigationHandler KeyboardNavigationHandler { get; } - - /// - /// Gets or sets the input element that the pointer is currently over. - /// - IInputElement? PointerOverElement { get; set; } - - /// - /// Gets or sets a value indicating whether access keys are shown in the window. - /// - bool ShowAccessKeys { get; set; } - - /// - /// Gets associated mouse device - /// - [CanBeNull] - IMouseDevice? MouseDevice { get; } - } -} diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Input/IKeyboardDevice.cs deleted file mode 100644 index 9506dc36fb..0000000000 --- a/src/Avalonia.Input/IKeyboardDevice.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.ComponentModel; - -namespace Avalonia.Input -{ - [Flags, Obsolete("Use KeyModifiers and PointerPointProperties")] - public enum InputModifiers - { - None = 0, - Alt = 1, - Control = 2, - Shift = 4, - Windows = 8, - LeftMouseButton = 16, - RightMouseButton = 32, - MiddleMouseButton = 64 - } - - [Flags] - public enum KeyModifiers - { - None = 0, - Alt = 1, - Control = 2, - Shift = 4, - Meta = 8, - } - - [Flags] - public enum KeyStates - { - None = 0, - Down = 1, - Toggled = 2, - } - - [Flags] - public enum RawInputModifiers - { - None = 0, - Alt = 1, - Control = 2, - Shift = 4, - Meta = 8, - LeftMouseButton = 16, - RightMouseButton = 32, - MiddleMouseButton = 64, - XButton1MouseButton = 128, - XButton2MouseButton = 256, - KeyboardMask = Alt | Control | Shift | Meta - } - - internal static class KeyModifiersUtils - { - public static KeyModifiers ConvertToKey(RawInputModifiers modifiers) => - (KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask); - } - - public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged - { - IInputElement? FocusedElement { get; } - - void SetFocusedElement( - IInputElement? element, - NavigationMethod method, - KeyModifiers modifiers); - } -} diff --git a/src/Avalonia.Input/IMouseDevice.cs b/src/Avalonia.Input/IMouseDevice.cs deleted file mode 100644 index 272d1eb8d7..0000000000 --- a/src/Avalonia.Input/IMouseDevice.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Avalonia.Input -{ - /// - /// Represents a mouse device. - /// - public interface IMouseDevice : IPointerDevice - { - /// - /// Gets the mouse position, in screen coordinates. - /// - [Obsolete("Use PointerEventArgs.GetPosition")] - PixelPoint Position { get; } - - void TopLevelClosed(IInputRoot root); - - void SceneInvalidated(IInputRoot root, Rect rect); - } -} diff --git a/src/Avalonia.Input/IPointerDevice.cs b/src/Avalonia.Input/IPointerDevice.cs deleted file mode 100644 index 1f82cb1ed7..0000000000 --- a/src/Avalonia.Input/IPointerDevice.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using Avalonia.VisualTree; - -namespace Avalonia.Input -{ - public interface IPointerDevice : IInputDevice - { - [Obsolete("Use IPointer")] - IInputElement? Captured { get; } - - [Obsolete("Use IPointer")] - void Capture(IInputElement? control); - - [Obsolete("Use PointerEventArgs.GetPosition")] - Point GetPosition(IVisual relativeTo); - } -} diff --git a/src/Avalonia.Input/KeyGesture.cs b/src/Avalonia.Input/KeyGesture.cs deleted file mode 100644 index 0adbe73263..0000000000 --- a/src/Avalonia.Input/KeyGesture.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Avalonia.Input -{ - /// - /// Defines a keyboard input combination. - /// - public sealed class KeyGesture : IEquatable - { - private static readonly Dictionary s_keySynonyms = new Dictionary - { - { "+", Key.OemPlus }, { "-", Key.OemMinus }, { ".", Key.OemPeriod }, { ",", Key.OemComma } - }; - - [Obsolete("Use constructor taking KeyModifiers")] - public KeyGesture(Key key, InputModifiers modifiers) - { - Key = key; - KeyModifiers = (KeyModifiers)(((int)modifiers) & 0xf); - } - - public KeyGesture(Key key, KeyModifiers modifiers = KeyModifiers.None) - { - Key = key; - KeyModifiers = modifiers; - } - - public bool Equals(KeyGesture? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - - return Key == other.Key && KeyModifiers == other.KeyModifiers; - } - - public override bool Equals(object? obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - - return obj is KeyGesture gesture && Equals(gesture); - } - - public override int GetHashCode() - { - unchecked - { - return ((int)Key * 397) ^ (int)KeyModifiers; - } - } - - public static bool operator ==(KeyGesture? left, KeyGesture? right) - { - return Equals(left, right); - } - - public static bool operator !=(KeyGesture? left, KeyGesture? right) - { - return !Equals(left, right); - } - - public Key Key { get; } - - [Obsolete("Use KeyModifiers")] - public InputModifiers Modifiers => (InputModifiers)KeyModifiers; - - public KeyModifiers KeyModifiers { get; } - - public static KeyGesture Parse(string gesture) - { - // string.Split can't be used here because "Ctrl++" is a perfectly valid key gesture - - var key = Key.None; - var keyModifiers = KeyModifiers.None; - - var cstart = 0; - - for (var c = 0; c <= gesture.Length; c++) - { - var ch = c == gesture.Length ? '\0' : gesture[c]; - bool isLast = c == gesture.Length; - - if (isLast || (ch == '+' && cstart != c)) - { - var partSpan = gesture.AsSpan(cstart, c - cstart).Trim(); - - if (isLast) - { - key = ParseKey(partSpan.ToString()); - } - else - { - keyModifiers |= ParseModifier(partSpan); - } - - cstart = c + 1; - } - } - - - return new KeyGesture(key, keyModifiers); - } - - public override string ToString() - { - var s = new StringBuilder(); - - static void Plus(StringBuilder s) - { - if (s.Length > 0) - { - s.Append("+"); - } - } - - if (KeyModifiers.HasAllFlags(KeyModifiers.Control)) - { - s.Append("Ctrl"); - } - - if (KeyModifiers.HasAllFlags(KeyModifiers.Shift)) - { - Plus(s); - s.Append("Shift"); - } - - if (KeyModifiers.HasAllFlags(KeyModifiers.Alt)) - { - Plus(s); - s.Append("Alt"); - } - - if (KeyModifiers.HasAllFlags(KeyModifiers.Meta)) - { - Plus(s); - s.Append("Cmd"); - } - - Plus(s); - s.Append(Key); - - return s.ToString(); - } - - public bool Matches(KeyEventArgs keyEvent) => - keyEvent != null && - keyEvent.KeyModifiers == KeyModifiers && - ResolveNumPadOperationKey(keyEvent.Key) == ResolveNumPadOperationKey(Key); - - // TODO: Move that to external key parser - private static Key ParseKey(string key) - { - if (s_keySynonyms.TryGetValue(key.ToLower(), out Key rv)) - return rv; - - return (Key)Enum.Parse(typeof(Key), key, true); - } - - private static KeyModifiers ParseModifier(ReadOnlySpan modifier) - { - if (modifier.Equals("ctrl".AsSpan(), StringComparison.OrdinalIgnoreCase)) - { - return KeyModifiers.Control; - } - - if (modifier.Equals("cmd".AsSpan(), StringComparison.OrdinalIgnoreCase) || - modifier.Equals("win".AsSpan(), StringComparison.OrdinalIgnoreCase) || - modifier.Equals("⌘".AsSpan(), StringComparison.OrdinalIgnoreCase)) - { - return KeyModifiers.Meta; - } - - return (KeyModifiers)Enum.Parse(typeof(KeyModifiers), modifier.ToString(), true); - } - - private Key ResolveNumPadOperationKey(Key key) - { - switch (key) - { - case Key.Add: - return Key.OemPlus; - case Key.Subtract: - return Key.OemMinus; - case Key.Decimal: - return Key.OemPeriod; - default: - return key; - } - } - } -} diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Input/KeyboardDevice.cs deleted file mode 100644 index 3df717b8c4..0000000000 --- a/src/Avalonia.Input/KeyboardDevice.cs +++ /dev/null @@ -1,249 +0,0 @@ -using System.ComponentModel; -using System.Runtime.CompilerServices; -using Avalonia.Input.Raw; -using Avalonia.Input.TextInput; -using Avalonia.Interactivity; -using Avalonia.VisualTree; - -namespace Avalonia.Input -{ - public class KeyboardDevice : IKeyboardDevice, INotifyPropertyChanged - { - private IInputElement? _focusedElement; - private IInputRoot? _focusedRoot; - - public event PropertyChangedEventHandler? PropertyChanged; - - public static IKeyboardDevice? Instance => AvaloniaLocator.Current.GetService(); - - public IInputManager? InputManager => AvaloniaLocator.Current.GetService(); - - public IFocusManager? FocusManager => AvaloniaLocator.Current.GetService(); - - // This should live in the FocusManager, but with the current outdated architecture - // the source of truth about the input focus is in KeyboardDevice - private readonly TextInputMethodManager _textInputManager = new TextInputMethodManager(); - - public IInputElement? FocusedElement => _focusedElement; - - private void ClearFocusWithinAncestors(IInputElement? element) - { - var el = element; - - while (el != null) - { - if (el is InputElement ie) - { - ie.IsKeyboardFocusWithin = false; - } - - el = (IInputElement?)el.VisualParent; - } - } - - private void ClearFocusWithin(IInputElement element, bool clearRoot) - { - foreach (var visual in element.VisualChildren) - { - if (visual is IInputElement el && el.IsKeyboardFocusWithin) - { - ClearFocusWithin(el, true); - break; - } - } - - if (clearRoot) - { - if (element is InputElement ie) - { - ie.IsKeyboardFocusWithin = false; - } - } - } - - private void SetIsFocusWithin(IInputElement? oldElement, IInputElement? newElement) - { - if (newElement == null && oldElement != null) - { - ClearFocusWithinAncestors(oldElement); - return; - } - - IInputElement? branch = null; - - var el = newElement; - - while (el != null) - { - if (el.IsKeyboardFocusWithin) - { - branch = el; - break; - } - - el = el.VisualParent as IInputElement; - } - - el = oldElement; - - if (el != null && branch != null) - { - ClearFocusWithin(branch, false); - } - - el = newElement; - - while (el != null && el != branch) - { - if (el is InputElement ie) - { - ie.IsKeyboardFocusWithin = true; - } - - el = el.VisualParent as IInputElement; - } - } - - private void ClearChildrenFocusWithin(IInputElement element, bool clearRoot) - { - foreach (var visual in element.VisualChildren) - { - if (visual is IInputElement el && el.IsKeyboardFocusWithin) - { - ClearChildrenFocusWithin(el, true); - break; - } - } - - if (clearRoot && element is InputElement ie) - { - ie.IsKeyboardFocusWithin = false; - } - } - - public void SetFocusedElement( - IInputElement? element, - NavigationMethod method, - KeyModifiers keyModifiers) - { - if (element != FocusedElement) - { - var interactive = FocusedElement as IInteractive; - - if (FocusedElement != null && - (!FocusedElement.IsAttachedToVisualTree || - _focusedRoot != element?.VisualRoot as IInputRoot) && - _focusedRoot != null) - { - ClearChildrenFocusWithin(_focusedRoot, true); - } - - SetIsFocusWithin(FocusedElement, element); - _focusedElement = element; - _focusedRoot = _focusedElement?.VisualRoot as IInputRoot; - - interactive?.RaiseEvent(new RoutedEventArgs - { - RoutedEvent = InputElement.LostFocusEvent, - }); - - interactive = element as IInteractive; - - interactive?.RaiseEvent(new GotFocusEventArgs - { - RoutedEvent = InputElement.GotFocusEvent, - NavigationMethod = method, - KeyModifiers = keyModifiers, - }); - - _textInputManager.SetFocusedElement(element); - RaisePropertyChanged(nameof(FocusedElement)); - } - } - - protected void RaisePropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - public void ProcessRawEvent(RawInputEventArgs e) - { - if(e.Handled) - return; - - var element = FocusedElement ?? e.Root; - - if (e is RawKeyEventArgs keyInput) - { - switch (keyInput.Type) - { - case RawKeyEventType.KeyDown: - case RawKeyEventType.KeyUp: - var routedEvent = keyInput.Type == RawKeyEventType.KeyDown - ? InputElement.KeyDownEvent - : InputElement.KeyUpEvent; - - KeyEventArgs ev = new KeyEventArgs - { - RoutedEvent = routedEvent, - Device = this, - Key = keyInput.Key, - KeyModifiers = KeyModifiersUtils.ConvertToKey(keyInput.Modifiers), - Source = element, - }; - - IVisual? currentHandler = element; - while (currentHandler != null && !ev.Handled && keyInput.Type == RawKeyEventType.KeyDown) - { - var bindings = (currentHandler as IInputElement)?.KeyBindings; - if (bindings != null) - { - KeyBinding[]? bindingsCopy = null; - - // Create a copy of the KeyBindings list if there's a binding which matches the event. - // If we don't do this the foreach loop will throw an InvalidOperationException when the KeyBindings list is changed. - // This can happen when a new view is loaded which adds its own KeyBindings to the handler. - foreach (var binding in bindings) - { - if (binding.Gesture?.Matches(ev) == true) - { - bindingsCopy = bindings.ToArray(); - break; - } - } - - if (bindingsCopy is object) - { - foreach (var binding in bindingsCopy) - { - if (ev.Handled) - break; - binding.TryHandle(ev); - } - } - } - currentHandler = currentHandler.VisualParent; - } - - element.RaiseEvent(ev); - e.Handled = ev.Handled; - break; - } - } - - if (e is RawTextInputEventArgs text) - { - var ev = new TextInputEventArgs() - { - Device = this, - Text = text.Text, - Source = element, - RoutedEvent = InputElement.TextInputEvent - }; - - element.RaiseEvent(ev); - e.Handled = ev.Handled; - } - } - } -} diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs deleted file mode 100644 index a5d54bb047..0000000000 --- a/src/Avalonia.Input/MouseDevice.cs +++ /dev/null @@ -1,571 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reactive.Linq; -using Avalonia.Input.Raw; -using Avalonia.Interactivity; -using Avalonia.Platform; -using Avalonia.Utilities; -using Avalonia.VisualTree; - -namespace Avalonia.Input -{ - /// - /// Represents a mouse device. - /// - public class MouseDevice : IMouseDevice, IDisposable - { - private int _clickCount; - private Rect _lastClickRect; - private ulong _lastClickTime; - - private readonly Pointer _pointer; - private bool _disposed; - private PixelPoint? _position; - - public MouseDevice(Pointer? pointer = null) - { - _pointer = pointer ?? new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); - } - - /// - /// Gets the control that is currently capturing by the mouse, if any. - /// - /// - /// When an element captures the mouse, it receives mouse input whether the cursor is - /// within the control's bounds or not. To set the mouse capture, call the - /// method. - /// - [Obsolete("Use IPointer instead")] - public IInputElement? Captured => _pointer.Captured; - - /// - /// Gets the mouse position, in screen coordinates. - /// - [Obsolete("Use events instead")] - public PixelPoint Position - { - get => _position ?? new PixelPoint(-1, -1); - protected set => _position = value; - } - - /// - /// Captures mouse input to the specified control. - /// - /// The control. - /// - /// When an element captures the mouse, it receives mouse input whether the cursor is - /// within the control's bounds or not. The current mouse capture control is exposed - /// by the property. - /// - public void Capture(IInputElement? control) - { - _pointer.Capture(control); - } - - /// - /// Gets the mouse position relative to a control. - /// - /// The control. - /// The mouse position in the control's coordinates. - public Point GetPosition(IVisual relativeTo) - { - relativeTo = relativeTo ?? throw new ArgumentNullException(nameof(relativeTo)); - - if (relativeTo.VisualRoot == null) - { - throw new InvalidOperationException("Control is not attached to visual tree."); - } - -#pragma warning disable CS0618 // Type or member is obsolete - var rootPoint = relativeTo.VisualRoot.PointToClient(Position); -#pragma warning restore CS0618 // Type or member is obsolete - var transform = relativeTo.VisualRoot.TransformToVisual(relativeTo); - return rootPoint * transform!.Value; - } - - public void ProcessRawEvent(RawInputEventArgs e) - { - if (!e.Handled && e is RawPointerEventArgs margs) - ProcessRawEvent(margs); - } - - public void TopLevelClosed(IInputRoot root) - { - ClearPointerOver(this, 0, root, PointerPointProperties.None, KeyModifiers.None); - } - - public void SceneInvalidated(IInputRoot root, Rect rect) - { - // Pointer is outside of the target area - if (_position == null ) - { - if (root.PointerOverElement != null) - ClearPointerOver(this, 0, root, PointerPointProperties.None, KeyModifiers.None); - return; - } - - - var clientPoint = root.PointToClient(_position.Value); - - if (rect.Contains(clientPoint)) - { - if (_pointer.Captured == null) - { - SetPointerOver(this, 0 /* TODO: proper timestamp */, root, clientPoint, - PointerPointProperties.None, KeyModifiers.None); - } - else - { - SetPointerOver(this, 0 /* TODO: proper timestamp */, root, _pointer.Captured, - PointerPointProperties.None, KeyModifiers.None); - } - } - } - - int ButtonCount(PointerPointProperties props) - { - var rv = 0; - if (props.IsLeftButtonPressed) - rv++; - if (props.IsMiddleButtonPressed) - rv++; - if (props.IsRightButtonPressed) - rv++; - if (props.IsXButton1Pressed) - rv++; - if (props.IsXButton2Pressed) - rv++; - return rv; - } - - private void ProcessRawEvent(RawPointerEventArgs e) - { - e = e ?? throw new ArgumentNullException(nameof(e)); - - var mouse = (MouseDevice)e.Device; - if(mouse._disposed) - return; - - if (e.Type == RawPointerEventType.NonClientLeftButtonDown) return; - - _position = e.Root.PointToScreen(e.Position); - var props = CreateProperties(e); - var keyModifiers = KeyModifiersUtils.ConvertToKey(e.InputModifiers); - switch (e.Type) - { - case RawPointerEventType.LeaveWindow: - LeaveWindow(mouse, e.Timestamp, e.Root, props, keyModifiers); - break; - case RawPointerEventType.LeftButtonDown: - case RawPointerEventType.RightButtonDown: - case RawPointerEventType.MiddleButtonDown: - case RawPointerEventType.XButton1Down: - case RawPointerEventType.XButton2Down: - if (ButtonCount(props) > 1) - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints); - else - e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position, - props, keyModifiers); - break; - case RawPointerEventType.LeftButtonUp: - case RawPointerEventType.RightButtonUp: - case RawPointerEventType.MiddleButtonUp: - case RawPointerEventType.XButton1Up: - case RawPointerEventType.XButton2Up: - if (ButtonCount(props) != 0) - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints); - else - e.Handled = MouseUp(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); - break; - case RawPointerEventType.Move: - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints); - break; - case RawPointerEventType.Wheel: - e.Handled = MouseWheel(mouse, e.Timestamp, e.Root, e.Position, props, ((RawMouseWheelEventArgs)e).Delta, keyModifiers); - break; - case RawPointerEventType.Magnify: - e.Handled = GestureMagnify(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); - break; - case RawPointerEventType.Rotate: - e.Handled = GestureRotate(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); - break; - case RawPointerEventType.Swipe: - e.Handled = GestureSwipe(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); - break; - } - } - - private void LeaveWindow(IMouseDevice device, ulong timestamp, IInputRoot root, PointerPointProperties properties, - KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - _position = null; - ClearPointerOver(this, timestamp, root, properties, inputModifiers); - } - - - PointerPointProperties CreateProperties(RawPointerEventArgs args) - { - - var kind = PointerUpdateKind.Other; - - if (args.Type == RawPointerEventType.LeftButtonDown) - kind = PointerUpdateKind.LeftButtonPressed; - if (args.Type == RawPointerEventType.MiddleButtonDown) - kind = PointerUpdateKind.MiddleButtonPressed; - if (args.Type == RawPointerEventType.RightButtonDown) - kind = PointerUpdateKind.RightButtonPressed; - if (args.Type == RawPointerEventType.XButton1Down) - kind = PointerUpdateKind.XButton1Pressed; - if (args.Type == RawPointerEventType.XButton2Down) - kind = PointerUpdateKind.XButton2Pressed; - if (args.Type == RawPointerEventType.LeftButtonUp) - kind = PointerUpdateKind.LeftButtonReleased; - if (args.Type == RawPointerEventType.MiddleButtonUp) - kind = PointerUpdateKind.MiddleButtonReleased; - if (args.Type == RawPointerEventType.RightButtonUp) - kind = PointerUpdateKind.RightButtonReleased; - if (args.Type == RawPointerEventType.XButton1Up) - kind = PointerUpdateKind.XButton1Released; - if (args.Type == RawPointerEventType.XButton2Up) - kind = PointerUpdateKind.XButton2Released; - - return new PointerPointProperties(args.InputModifiers, kind); - } - - private MouseButton _lastMouseDownButton; - private bool MouseDown(IMouseDevice device, ulong timestamp, IInputElement root, Point p, - PointerPointProperties properties, - KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var hit = HitTest(root, p); - - if (hit != null) - { - _pointer.Capture(hit); - var source = GetSource(hit); - if (source != null) - { - var settings = AvaloniaLocator.Current.GetService(); - var doubleClickTime = settings?.DoubleClickTime.TotalMilliseconds ?? 500; - var doubleClickSize = settings?.DoubleClickSize ?? new Size(4, 4); - - if (!_lastClickRect.Contains(p) || timestamp - _lastClickTime > doubleClickTime) - { - _clickCount = 0; - } - - ++_clickCount; - _lastClickTime = timestamp; - _lastClickRect = new Rect(p, new Size()) - .Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2)); - _lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton(); - var e = new PointerPressedEventArgs(source, _pointer, root, p, timestamp, properties, inputModifiers, _clickCount); - source.RaiseEvent(e); - return e.Handled; - } - } - - return false; - } - - private bool MouseMove(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties properties, - KeyModifiers inputModifiers, Lazy?>? intermediatePoints) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - IInputElement? source; - - if (_pointer.Captured == null) - { - source = SetPointerOver(this, timestamp, root, p, properties, inputModifiers); - } - else - { - SetPointerOver(this, timestamp, root, _pointer.Captured, properties, inputModifiers); - source = _pointer.Captured; - } - - if (source is object) - { - var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, root, - p, timestamp, properties, inputModifiers, intermediatePoints); - - source.RaiseEvent(e); - return e.Handled; - } - - return false; - } - - private bool MouseUp(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties props, - KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var hit = HitTest(root, p); - var source = GetSource(hit); - - if (source is not null) - { - var e = new PointerReleasedEventArgs(source, _pointer, root, p, timestamp, props, inputModifiers, - _lastMouseDownButton); - - source?.RaiseEvent(e); - _pointer.Capture(null); - return e.Handled; - } - - return false; - } - - private bool MouseWheel(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, - Vector delta, KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var hit = HitTest(root, p); - var source = GetSource(hit); - - // KeyModifiers.Shift should scroll in horizontal direction. This does not work on every platform. - // If Shift-Key is pressed and X is close to 0 we swap the Vector. - if (inputModifiers == KeyModifiers.Shift && MathUtilities.IsZero(delta.X)) - { - delta = new Vector(delta.Y, delta.X); - } - - if (source is not null) - { - var e = new PointerWheelEventArgs(source, _pointer, root, p, timestamp, props, inputModifiers, delta); - - source?.RaiseEvent(e); - return e.Handled; - } - - return false; - } - - private bool GestureMagnify(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var hit = HitTest(root, p); - - if (hit != null) - { - var source = GetSource(hit); - var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureMagnifyEvent, source, - _pointer, root, p, timestamp, props, inputModifiers, delta); - - source?.RaiseEvent(e); - return e.Handled; - } - - return false; - } - - private bool GestureRotate(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var hit = HitTest(root, p); - - if (hit != null) - { - var source = GetSource(hit); - var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureRotateEvent, source, - _pointer, root, p, timestamp, props, inputModifiers, delta); - - source?.RaiseEvent(e); - return e.Handled; - } - - return false; - } - - private bool GestureSwipe(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var hit = HitTest(root, p); - - if (hit != null) - { - var source = GetSource(hit); - var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureSwipeEvent, source, - _pointer, root, p, timestamp, props, inputModifiers, delta); - - source?.RaiseEvent(e); - return e.Handled; - } - - return false; - } - - private IInteractive? GetSource(IVisual? hit) - { - if (hit is null) - return null; - - return _pointer.Captured ?? - (hit as IInteractive) ?? - hit.GetSelfAndVisualAncestors().OfType().FirstOrDefault(); - } - - private IInputElement? HitTest(IInputElement root, Point p) - { - root = root ?? throw new ArgumentNullException(nameof(root)); - - return _pointer.Captured ?? root.InputHitTest(p); - } - - PointerEventArgs CreateSimpleEvent(RoutedEvent ev, ulong timestamp, IInteractive? source, - PointerPointProperties properties, - KeyModifiers inputModifiers) - { - return new PointerEventArgs(ev, source, _pointer, null, default, - timestamp, properties, inputModifiers); - } - - private void ClearPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, - PointerPointProperties properties, - KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var element = root.PointerOverElement; - var e = CreateSimpleEvent(InputElement.PointerLeaveEvent, timestamp, element, properties, inputModifiers); - - if (element!=null && !element.IsAttachedToVisualTree) - { - // element has been removed from visual tree so do top down cleanup - if (root.IsPointerOver) - ClearChildrenPointerOver(e, root,true); - } - while (element != null) - { - e.Source = element; - e.Handled = false; - element.RaiseEvent(e); - element = (IInputElement?)element.VisualParent; - } - - root.PointerOverElement = null; - } - - private void ClearChildrenPointerOver(PointerEventArgs e, IInputElement element,bool clearRoot) - { - foreach (IInputElement el in element.VisualChildren) - { - if (el.IsPointerOver) - { - ClearChildrenPointerOver(e, el, true); - break; - } - } - if(clearRoot) - { - e.Source = element; - e.Handled = false; - element.RaiseEvent(e); - } - } - - private IInputElement? SetPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties properties, - KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var element = root.InputHitTest(p); - - if (element != root.PointerOverElement) - { - if (element != null) - { - SetPointerOver(device, timestamp, root, element, properties, inputModifiers); - } - else - { - ClearPointerOver(device, timestamp, root, properties, inputModifiers); - } - } - - return element; - } - - private void SetPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, IInputElement element, - PointerPointProperties properties, - KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - element = element ?? throw new ArgumentNullException(nameof(element)); - - IInputElement? branch = null; - - IInputElement? el = element; - - while (el != null) - { - if (el.IsPointerOver) - { - branch = el; - break; - } - el = (IInputElement?)el.VisualParent; - } - - el = root.PointerOverElement; - - var e = CreateSimpleEvent(InputElement.PointerLeaveEvent, timestamp, el, properties, inputModifiers); - if (el!=null && branch!=null && !el.IsAttachedToVisualTree) - { - ClearChildrenPointerOver(e,branch,false); - } - - while (el != null && el != branch) - { - e.Source = el; - e.Handled = false; - el.RaiseEvent(e); - el = (IInputElement?)el.VisualParent; - } - - el = root.PointerOverElement = element; - e.RoutedEvent = InputElement.PointerEnterEvent; - - while (el != null && el != branch) - { - e.Source = el; - e.Handled = false; - el.RaiseEvent(e); - el = (IInputElement?)el.VisualParent; - } - } - - public void Dispose() - { - _disposed = true; - _pointer?.Dispose(); - } - } -} diff --git a/src/Avalonia.Input/PointerEventArgs.cs b/src/Avalonia.Input/PointerEventArgs.cs deleted file mode 100644 index 0604d09dc4..0000000000 --- a/src/Avalonia.Input/PointerEventArgs.cs +++ /dev/null @@ -1,210 +0,0 @@ -using System; -using System.Collections.Generic; -using Avalonia.Input.Raw; -using Avalonia.Interactivity; -using Avalonia.VisualTree; - -namespace Avalonia.Input -{ - public class PointerEventArgs : RoutedEventArgs - { - private readonly IVisual? _rootVisual; - private readonly Point _rootVisualPosition; - private readonly PointerPointProperties _properties; - private Lazy?>? _previousPoints; - - public PointerEventArgs(RoutedEvent routedEvent, - IInteractive? source, - IPointer pointer, - IVisual? rootVisual, Point rootVisualPosition, - ulong timestamp, - PointerPointProperties properties, - KeyModifiers modifiers) - : base(routedEvent) - { - Source = source; - _rootVisual = rootVisual; - _rootVisualPosition = rootVisualPosition; - _properties = properties; - Pointer = pointer; - Timestamp = timestamp; - KeyModifiers = modifiers; - } - - public PointerEventArgs(RoutedEvent routedEvent, - IInteractive? source, - IPointer pointer, - IVisual? rootVisual, Point rootVisualPosition, - ulong timestamp, - PointerPointProperties properties, - KeyModifiers modifiers, - Lazy?>? previousPoints) - : this(routedEvent, source, pointer, rootVisual, rootVisualPosition, timestamp, properties, modifiers) - { - _previousPoints = previousPoints; - } - - - class EmulatedDevice : IPointerDevice - { - private readonly PointerEventArgs _ev; - - public EmulatedDevice(PointerEventArgs ev) - { - _ev = ev; - } - - public void ProcessRawEvent(RawInputEventArgs ev) => throw new NotSupportedException(); - - public IInputElement? Captured => _ev.Pointer.Captured; - public void Capture(IInputElement? control) - { - _ev.Pointer.Capture(control); - } - - public Point GetPosition(IVisual relativeTo) => _ev.GetPosition(relativeTo); - } - - public IPointer Pointer { get; } - public ulong Timestamp { get; } - - private IPointerDevice? _device; - - [Obsolete("Use Pointer to get pointer-specific information")] - public IPointerDevice Device => _device ?? (_device = new EmulatedDevice(this)); - - [Obsolete("Use KeyModifiers and PointerPointProperties")] - public InputModifiers InputModifiers - { - get - { - var mods = (InputModifiers)KeyModifiers; - if (_properties.IsLeftButtonPressed) - mods |= InputModifiers.LeftMouseButton; - if (_properties.IsMiddleButtonPressed) - mods |= InputModifiers.MiddleMouseButton; - if (_properties.IsRightButtonPressed) - mods |= InputModifiers.RightMouseButton; - - return mods; - } - } - - public KeyModifiers KeyModifiers { get; } - - private Point GetPosition(Point pt, IVisual? relativeTo) - { - if (_rootVisual == null) - return default; - if (relativeTo == null) - return pt; - return pt * _rootVisual.TransformToVisual(relativeTo) ?? default; - } - - public Point GetPosition(IVisual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo); - - [Obsolete("Use GetCurrentPoint")] - public PointerPoint GetPointerPoint(IVisual? relativeTo) => GetCurrentPoint(relativeTo); - - /// - /// Returns the PointerPoint associated with the current event - /// - /// The visual which coordinate system to use. Pass null for toplevel coordinate system - /// - public PointerPoint GetCurrentPoint(IVisual? relativeTo) - => new PointerPoint(Pointer, GetPosition(relativeTo), _properties); - - /// - /// Returns the PointerPoint associated with the current event - /// - /// The visual which coordinate system to use. Pass null for toplevel coordinate system - /// - public IReadOnlyList GetIntermediatePoints(IVisual? relativeTo) - { - var previousPoints = _previousPoints?.Value; - if (previousPoints == null || previousPoints.Count == 0) - return new[] { GetCurrentPoint(relativeTo) }; - var points = new PointerPoint[previousPoints.Count + 1]; - for (var c = 0; c < previousPoints.Count; c++) - { - var pt = previousPoints[c]; - points[c] = new PointerPoint(Pointer, GetPosition(pt.Position, relativeTo), _properties); - } - - points[points.Length - 1] = GetCurrentPoint(relativeTo); - return points; - } - - /// - /// Returns the current pointer point properties - /// - protected PointerPointProperties Properties => _properties; - } - - public enum MouseButton - { - None, - Left, - Right, - Middle, - XButton1, - XButton2 - } - - public class PointerPressedEventArgs : PointerEventArgs - { - private readonly int _clickCount; - - public PointerPressedEventArgs( - IInteractive source, - IPointer pointer, - IVisual rootVisual, Point rootVisualPosition, - ulong timestamp, - PointerPointProperties properties, - KeyModifiers modifiers, - int clickCount = 1) - : base(InputElement.PointerPressedEvent, source, pointer, rootVisual, rootVisualPosition, - timestamp, properties, modifiers) - { - _clickCount = clickCount; - } - - public int ClickCount => _clickCount; - - [Obsolete("Use PointerPressedEventArgs.GetCurrentPoint(this).Properties")] - public MouseButton MouseButton => Properties.PointerUpdateKind.GetMouseButton(); - } - - public class PointerReleasedEventArgs : PointerEventArgs - { - public PointerReleasedEventArgs( - IInteractive source, IPointer pointer, - IVisual rootVisual, Point rootVisualPosition, ulong timestamp, - PointerPointProperties properties, KeyModifiers modifiers, - MouseButton initialPressMouseButton) - : base(InputElement.PointerReleasedEvent, source, pointer, rootVisual, rootVisualPosition, - timestamp, properties, modifiers) - { - InitialPressMouseButton = initialPressMouseButton; - } - - /// - /// Gets the mouse button that triggered the corresponding PointerPressed event - /// - public MouseButton InitialPressMouseButton { get; } - - [Obsolete("Use InitialPressMouseButton")] - public MouseButton MouseButton => InitialPressMouseButton; - } - - public class PointerCaptureLostEventArgs : RoutedEventArgs - { - public IPointer Pointer { get; } - - public PointerCaptureLostEventArgs(IInteractive source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent) - { - Pointer = pointer; - Source = source; - } - } -} diff --git a/src/Avalonia.Input/Properties/AssemblyInfo.cs b/src/Avalonia.Input/Properties/AssemblyInfo.cs deleted file mode 100644 index 6a68bf60d1..0000000000 --- a/src/Avalonia.Input/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -using System.Reflection; -using Avalonia.Metadata; - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input.TextInput")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input.GestureRecognizers")] diff --git a/src/Avalonia.Input/Raw/RawDragEvent.cs b/src/Avalonia.Input/Raw/RawDragEvent.cs deleted file mode 100644 index 6e9ce20ff1..0000000000 --- a/src/Avalonia.Input/Raw/RawDragEvent.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; - -namespace Avalonia.Input.Raw -{ - public class RawDragEvent : RawInputEventArgs - { - public Point Location { get; set; } - public IDataObject Data { get; } - public DragDropEffects Effects { get; set; } - public RawDragEventType Type { get; } - [Obsolete("Use KeyModifiers")] - public InputModifiers Modifiers { get; } - public KeyModifiers KeyModifiers { get; } - - public RawDragEvent(IDragDropDevice inputDevice, RawDragEventType type, - IInputRoot root, Point location, IDataObject data, DragDropEffects effects, RawInputModifiers modifiers) - :base(inputDevice, 0, root) - { - Type = type; - Location = location; - Data = data; - Effects = effects; - KeyModifiers = KeyModifiersUtils.ConvertToKey(modifiers); -#pragma warning disable CS0618 // Type or member is obsolete - Modifiers = (InputModifiers)modifiers; -#pragma warning restore CS0618 // Type or member is obsolete - } - } -} diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs deleted file mode 100644 index c157fa059c..0000000000 --- a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Avalonia.Input.Raw -{ - public enum RawPointerEventType - { - LeaveWindow, - LeftButtonDown, - LeftButtonUp, - RightButtonDown, - RightButtonUp, - MiddleButtonDown, - MiddleButtonUp, - XButton1Down, - XButton1Up, - XButton2Down, - XButton2Up, - Move, - Wheel, - NonClientLeftButtonDown, - TouchBegin, - TouchUpdate, - TouchEnd, - TouchCancel, - Magnify, - Rotate, - Swipe - } - - /// - /// A raw mouse event. - /// - public class RawPointerEventArgs : RawInputEventArgs - { - private RawPointerPoint _point; - - /// - /// Initializes a new instance of the class. - /// - /// The associated device. - /// The event timestamp. - /// The root from which the event originates. - /// The type of the event. - /// The mouse position, in client DIPs. - /// The input modifiers. - public RawPointerEventArgs( - IInputDevice device, - ulong timestamp, - IInputRoot root, - RawPointerEventType type, - Point position, - RawInputModifiers inputModifiers) - : base(device, timestamp, root) - { - Contract.Requires(device != null); - Contract.Requires(root != null); - - Position = position; - Type = type; - InputModifiers = inputModifiers; - } - - /// - /// Initializes a new instance of the class. - /// - /// The associated device. - /// The event timestamp. - /// The root from which the event originates. - /// The type of the event. - /// The point properties and position, in client DIPs. - /// The input modifiers. - public RawPointerEventArgs( - IInputDevice device, - ulong timestamp, - IInputRoot root, - RawPointerEventType type, - RawPointerPoint point, - RawInputModifiers inputModifiers) - : base(device, timestamp, root) - { - Contract.Requires(device != null); - Contract.Requires(root != null); - - Point = point; - Type = type; - InputModifiers = inputModifiers; - } - - /// - /// Gets the pointer properties and position, in client DIPs. - /// - public RawPointerPoint Point - { - get => _point; - set => _point = value; - } - - /// - /// Gets the mouse position, in client DIPs. - /// - public Point Position - { - get => _point.Position; - set => _point.Position = value; - } - - /// - /// Gets the type of the event. - /// - public RawPointerEventType Type { get; set; } - - /// - /// Gets the input modifiers. - /// - public RawInputModifiers InputModifiers { get; set; } - - /// - /// Points that were traversed by a pointer since the previous relevant event, - /// only valid for Move and TouchUpdate - /// - public Lazy?>? IntermediatePoints { get; set; } - } - - public struct RawPointerPoint - { - /// - /// Pointer position, in client DIPs. - /// - public Point Position { get; set; } - - public RawPointerPoint() - { - Position = default; - } - } -} diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Input/TouchDevice.cs deleted file mode 100644 index 12ad182bf8..0000000000 --- a/src/Avalonia.Input/TouchDevice.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Avalonia.Input.Raw; -using Avalonia.Platform; - -namespace Avalonia.Input -{ - /// - /// Handles raw touch events - /// - /// This class is supposed to be used on per-toplevel basis, don't use a shared one - /// - /// - public class TouchDevice : IInputDevice, IDisposable - { - private readonly Dictionary _pointers = new Dictionary(); - private bool _disposed; - private int _clickCount; - private Rect _lastClickRect; - private ulong _lastClickTime; - KeyModifiers GetKeyModifiers(RawInputModifiers modifiers) => - (KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask); - - RawInputModifiers GetModifiers(RawInputModifiers modifiers, bool isLeftButtonDown) - { - var rv = modifiers &= RawInputModifiers.KeyboardMask; - if (isLeftButtonDown) - rv |= RawInputModifiers.LeftMouseButton; - return rv; - } - - public void ProcessRawEvent(RawInputEventArgs ev) - { - if (_disposed) - return; - var args = (RawTouchEventArgs)ev; - if (!_pointers.TryGetValue(args.TouchPointId, out var pointer)) - { - if (args.Type == RawPointerEventType.TouchEnd) - return; - var hit = args.Root.InputHitTest(args.Position); - - _pointers[args.TouchPointId] = pointer = new Pointer(Pointer.GetNextFreeId(), - PointerType.Touch, _pointers.Count == 0); - pointer.Capture(hit); - } - - - var target = pointer.Captured ?? args.Root; - if (args.Type == RawPointerEventType.TouchBegin) - { - if (_pointers.Count > 1) - { - _clickCount = 1; - _lastClickTime = 0; - _lastClickRect = new Rect(); - } - else - { - var settings = AvaloniaLocator.Current.GetRequiredService(); - - if (!_lastClickRect.Contains(args.Position) - || ev.Timestamp - _lastClickTime > settings.TouchDoubleClickTime.TotalMilliseconds) - { - _clickCount = 0; - } - ++_clickCount; - _lastClickTime = ev.Timestamp; - _lastClickRect = new Rect(args.Position, new Size()) - .Inflate(new Thickness(settings.TouchDoubleClickSize.Width / 2, settings.TouchDoubleClickSize.Height / 2)); - } - - target.RaiseEvent(new PointerPressedEventArgs(target, pointer, - args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, true), - PointerUpdateKind.LeftButtonPressed), - GetKeyModifiers(args.InputModifiers), _clickCount)); - } - - if (args.Type == RawPointerEventType.TouchEnd) - { - _pointers.Remove(args.TouchPointId); - using (pointer) - { - target.RaiseEvent(new PointerReleasedEventArgs(target, pointer, - args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, false), - PointerUpdateKind.LeftButtonReleased), - GetKeyModifiers(args.InputModifiers), MouseButton.Left)); - } - } - - if (args.Type == RawPointerEventType.TouchCancel) - { - _pointers.Remove(args.TouchPointId); - using (pointer) - pointer.Capture(null); - } - - if (args.Type == RawPointerEventType.TouchUpdate) - { - var modifiers = GetModifiers(args.InputModifiers, pointer.IsPrimary); - target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, args.Root, - args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, true), PointerUpdateKind.Other), - GetKeyModifiers(args.InputModifiers), args.IntermediatePoints)); - } - - - } - - public void Dispose() - { - if (_disposed) - return; - var values = _pointers.Values.ToList(); - _pointers.Clear(); - _disposed = true; - foreach (var p in values) - p.Dispose(); - } - - } -} diff --git a/src/Avalonia.Interactivity/ApiCompatBaseline.txt b/src/Avalonia.Interactivity/ApiCompatBaseline.txt deleted file mode 100644 index fcc74cf864..0000000000 --- a/src/Avalonia.Interactivity/ApiCompatBaseline.txt +++ /dev/null @@ -1 +0,0 @@ -Total Issues: 0 diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj deleted file mode 100644 index 03a53f7d86..0000000000 --- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Layout/ApiCompatBaseline.txt b/src/Avalonia.Layout/ApiCompatBaseline.txt deleted file mode 100644 index fcc74cf864..0000000000 --- a/src/Avalonia.Layout/ApiCompatBaseline.txt +++ /dev/null @@ -1 +0,0 @@ -Total Issues: 0 diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj deleted file mode 100644 index f2c4b0540b..0000000000 --- a/src/Avalonia.Layout/Avalonia.Layout.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - diff --git a/src/Avalonia.Layout/Properties/AssemblyInfo.cs b/src/Avalonia.Layout/Properties/AssemblyInfo.cs deleted file mode 100644 index efcbf184b5..0000000000 --- a/src/Avalonia.Layout/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Runtime.CompilerServices; -using Avalonia.Metadata; - -[assembly: InternalsVisibleTo("Avalonia.Layout.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Layout")] - diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj index 70ab38e786..2001a2fcbc 100644 --- a/src/Avalonia.Native/Avalonia.Native.csproj +++ b/src/Avalonia.Native/Avalonia.Native.csproj @@ -28,4 +28,6 @@ + + diff --git a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj index 2b32926008..76924d060f 100644 --- a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj +++ b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj @@ -8,6 +8,7 @@ - - + + + diff --git a/src/Avalonia.PlatformSupport/AssetLoader.cs b/src/Avalonia.PlatformSupport/AssetLoader.cs index fb03ec2f6e..0e33c3d4c7 100644 --- a/src/Avalonia.PlatformSupport/AssetLoader.cs +++ b/src/Avalonia.PlatformSupport/AssetLoader.cs @@ -14,14 +14,14 @@ namespace Avalonia.PlatformSupport /// public class AssetLoader : IAssetLoader { - private static AssemblyDescriptorResolver s_assemblyDescriptorResolver = new(); + private static IAssemblyDescriptorResolver s_assemblyDescriptorResolver = new AssemblyDescriptorResolver(); private AssemblyDescriptor? _defaultResmAssembly; /// /// Introduced for tests. /// - internal static void SetAssemblyDescriptorResolver(AssemblyDescriptorResolver resolver) => + internal static void SetAssemblyDescriptorResolver(IAssemblyDescriptorResolver resolver) => s_assemblyDescriptorResolver = resolver; /// @@ -182,13 +182,13 @@ namespace Avalonia.PlatformSupport throw new ArgumentException($"Unsupported url type: " + uri.Scheme, nameof(uri)); } - private (AssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri) + private (IAssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri) { var asm = s_assemblyDescriptorResolver.GetAssembly(uri.Authority); return (asm, uri.GetUnescapeAbsolutePath()); } - private AssemblyDescriptor? GetAssembly(Uri? uri) + private IAssemblyDescriptor? GetAssembly(Uri? uri) { if (uri != null) { diff --git a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj index 420ac0796c..5336f1e630 100644 --- a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj +++ b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj @@ -19,6 +19,6 @@ - + diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs index a3de7f2b8a..64ffec8482 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs +++ b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs @@ -6,7 +6,15 @@ using Avalonia.Utilities; namespace Avalonia.PlatformSupport.Internal; -internal class AssemblyDescriptor +internal interface IAssemblyDescriptor +{ + Assembly Assembly { get; } + Dictionary? Resources { get; } + Dictionary? AvaloniaResources { get; } + string? Name { get; } +} + +internal class AssemblyDescriptor : IAssemblyDescriptor { public AssemblyDescriptor(Assembly assembly) { diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs index a78051a9c4..28ae35d57d 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs +++ b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs @@ -5,11 +5,16 @@ using System.Reflection; namespace Avalonia.PlatformSupport.Internal; -internal class AssemblyDescriptorResolver +internal interface IAssemblyDescriptorResolver { - private readonly Dictionary _assemblyNameCache = new(); + IAssemblyDescriptor GetAssembly(string name); +} + +internal class AssemblyDescriptorResolver: IAssemblyDescriptorResolver +{ + private readonly Dictionary _assemblyNameCache = new(); - public AssemblyDescriptor GetAssembly(string name) + public IAssemblyDescriptor GetAssembly(string name) { if (name == null) throw new ArgumentNullException(nameof(name)); diff --git a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj index 5a8301a2e9..d746c6db7e 100644 --- a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj +++ b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj @@ -7,7 +7,7 @@ Avalonia.Remote.Protocol - + \ No newline at end of file diff --git a/src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj b/src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj new file mode 100644 index 0000000000..97e58f8a64 --- /dev/null +++ b/src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + enable + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + diff --git a/src/Avalonia.SourceGenerator/IsExternalInit.cs b/src/Avalonia.SourceGenerator/IsExternalInit.cs new file mode 100644 index 0000000000..c6ddf762ad --- /dev/null +++ b/src/Avalonia.SourceGenerator/IsExternalInit.cs @@ -0,0 +1,14 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [ExcludeFromCodeCoverage, DebuggerNonUserCode] + internal static class IsExternalInit + { + } +} diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs new file mode 100644 index 0000000000..4fc9397e7a --- /dev/null +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Avalonia.SourceGenerator +{ + [Generator(LanguageNames.CSharp)] + public class SubtypesFactoryGenerator : IIncrementalGenerator + { + private record struct MethodTarget(IMethodSymbol Method, string MethodDecl, ITypeSymbol BaseType, string Namespace); + private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName; + + private static bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) + { + return type.BaseType is not null && (SymbolEqualityComparer.Default.Equals(type.BaseType, baseType) || IsSubtypeOf(type.BaseType, baseType)); + } + + private static void GenerateSubTypes(SourceProductionContext context, MethodTarget methodTarget, ImmutableArray types) + { + var (method, methodDecl, baseType, @namespace) = methodTarget; + var candidateTypes = types.Where(i => IsSubtypeOf(i, baseType)).Where(i => $"{i.ContainingNamespace}.".StartsWith($"{@namespace}.")).ToArray(); + var type = method.ContainingType; + var isGeneric = type.TypeParameters.Length > 0; + var isClass = type.TypeKind == TypeKind.Class; + + var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}"; + var source = $@"using System; +using System.Collections.Generic; + +namespace {method.ContainingNamespace} +{{ + {typeDecl} + {{ + {methodDecl} + {{ + var hasMatch = false; + (hasMatch, {method.Parameters[1].Name}) = {method.Parameters[0].Name} switch + {{ +{string.Join("\n", candidateTypes.Select(i => $" \"{i.Name}\" => (true, ({method.Parameters[1].Type})new {i}()),"))} + _ => (false, default({method.Parameters[1].Type})) + }}; + + return hasMatch; + }} + }} +}}"; + + context.AddSource($"{type}.{method.MetadataName}.gen.cs", source); + } + + private static MethodTarget? PopulateMethodTargets(GeneratorSyntaxContext context, CancellationToken token) + { + token.ThrowIfCancellationRequested(); + if (context.Node is MethodDeclarationSyntax method) + { + var attributes = method.AttributeLists.SelectMany(i => i.Attributes); + var semanticModel = context.SemanticModel; + foreach (var attribute in attributes) + { + var attributeTypeInfo = semanticModel.GetTypeInfo(attribute); + if (attributeTypeInfo.Type is null || + attributeTypeInfo.Type.ToString() != s_attributeName || + attribute.ArgumentList is null) + { + continue; + } + + var arguments = attribute.ArgumentList.Arguments; + if (arguments.Count != 2) + { + continue; + } + + if (arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpr || + arguments[1].Expression is not LiteralExpressionSyntax and not IdentifierNameSyntax) + { + continue; + } + + var type = semanticModel.GetTypeInfo(typeOfExpr.Type); + var ns = semanticModel.GetConstantValue(arguments[1].Expression); + var methodDeclInfo = semanticModel.GetDeclaredSymbol(method); + + if (type.Type is not ITypeSymbol baseType || + ns.HasValue is false || + ns.Value is not string nsValue || + methodDeclInfo is not IMethodSymbol methodSymbol || + methodSymbol.Parameters.Length != 2 || + methodSymbol.Parameters[1].RefKind != RefKind.Out) + { + continue; + } + + var parameters = new SeparatedSyntaxList().AddRange(method.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); + var methodDecl = method + .WithAttributeLists(new SyntaxList()) + .WithParameterList(method.ParameterList.WithParameters(parameters)) + .WithBody(null) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) + .WithoutTrivia().ToString(); + + return new MethodTarget(methodSymbol, methodDecl, baseType, nsValue); + } + } + + return null; + } + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var typesProvider = context.SyntaxProvider.CreateSyntaxProvider( + static (syntaxNode, token) => + { + token.ThrowIfCancellationRequested(); + return syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax; + }, + static (syntaxContext, token) => + { + token.ThrowIfCancellationRequested(); + return syntaxContext.Node is ClassDeclarationSyntax or StructDeclarationSyntax && + syntaxContext.SemanticModel.GetDeclaredSymbol(syntaxContext.Node) is ITypeSymbol typeSymbol + ? typeSymbol : null; + }) + .SelectMany((type, token) => + { + token.ThrowIfCancellationRequested(); + return type is null ? Array.Empty() : new ITypeSymbol[] { type }; + }); + + var methodsProvider = context.SyntaxProvider.CreateSyntaxProvider( + static (syntaxNode, token) => + { + token.ThrowIfCancellationRequested(); + return syntaxNode is MethodDeclarationSyntax { AttributeLists.Count: > 0 }; + }, PopulateMethodTargets) + .SelectMany((method, token) => + { + token.ThrowIfCancellationRequested(); + return method is null ? Array.Empty() : new MethodTarget[] { method.Value }; + }); + + var generateContext = methodsProvider.Combine(typesProvider.Collect()); + + context.RegisterSourceOutput(generateContext, static (sourceContext, source) => + { + sourceContext.CancellationToken.ThrowIfCancellationRequested(); + GenerateSubTypes(sourceContext, source.Left, source.Right); + }); + } + } +} diff --git a/src/Avalonia.Styling/ApiCompatBaseline.txt b/src/Avalonia.Styling/ApiCompatBaseline.txt deleted file mode 100644 index 0eedc3e360..0000000000 --- a/src/Avalonia.Styling/ApiCompatBaseline.txt +++ /dev/null @@ -1,4 +0,0 @@ -Compat issues with assembly Avalonia.Styling: -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Styling.IStyleInstance.IsActive' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Styling.IStyleInstance.IsActive.get()' is present in the implementation but not in the contract. -Total Issues: 2 diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj deleted file mode 100644 index 139ba1bd2e..0000000000 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - net6.0;netstandard2.0 - Avalonia.Styling - Avalonia - - - - - - - - diff --git a/src/Avalonia.Styling/Controls/ChildNameScope.cs b/src/Avalonia.Styling/Controls/ChildNameScope.cs deleted file mode 100644 index 58114a57fd..0000000000 --- a/src/Avalonia.Styling/Controls/ChildNameScope.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Threading.Tasks; -using Avalonia.Utilities; - -namespace Avalonia.Controls -{ - public class ChildNameScope : INameScope - { - private readonly INameScope _parentScope; - private readonly NameScope _inner = new NameScope(); - - public ChildNameScope(INameScope parentScope) - { - _parentScope = parentScope; - } - - public void Register(string name, object element) => _inner.Register(name, element); - - public SynchronousCompletionAsyncResult FindAsync(string name) - { - var found = Find(name); - if (found != null) - return new SynchronousCompletionAsyncResult(found); - // Not found and both current and parent scope are in completed state - if(IsCompleted) - return new SynchronousCompletionAsyncResult(null); - return DoFindAsync(name); - } - - public SynchronousCompletionAsyncResult DoFindAsync(string name) - { - var src = new SynchronousCompletionAsyncResultSource(); - - void ParentSearch() - { - var parentSearch = _parentScope.FindAsync(name); - if (parentSearch.IsCompleted) - src.SetResult(parentSearch.GetResult()); - else - parentSearch.OnCompleted(() => src.SetResult(parentSearch.GetResult())); - } - if (!_inner.IsCompleted) - { - // Guaranteed to be incomplete at this point - var innerSearch = _inner.FindAsync(name); - innerSearch.OnCompleted(() => - { - var value = innerSearch.GetResult(); - if (value != null) - src.SetResult(value); - else ParentSearch(); - }); - } - else - ParentSearch(); - - return src.AsyncResult; - } - - public object? Find(string name) - { - var found = _inner.Find(name); - if (found != null) - return found; - if (_inner.IsCompleted) - return _parentScope.Find(name); - return null; - } - - public void Complete() => _inner.Complete(); - - public bool IsCompleted => _inner.IsCompleted && _parentScope.IsCompleted; - } -} diff --git a/src/Avalonia.Styling/Controls/NameScope.cs b/src/Avalonia.Styling/Controls/NameScope.cs deleted file mode 100644 index 77f98f85c4..0000000000 --- a/src/Avalonia.Styling/Controls/NameScope.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Avalonia.LogicalTree; -using Avalonia.Utilities; - -namespace Avalonia.Controls -{ - /// - /// Implements a name scope. - /// - public class NameScope : INameScope - { - /// - /// Defines the NameScope attached property. - /// - public static readonly AttachedProperty NameScopeProperty = - AvaloniaProperty.RegisterAttached("NameScope"); - - /// - public bool IsCompleted { get; private set; } - - private readonly Dictionary _inner = new Dictionary(); - - private readonly Dictionary> _pendingSearches = - new Dictionary>(); - - /// - /// Gets the value of the attached on a styled element. - /// - /// The styled element. - /// The value of the NameScope attached property. - public static INameScope GetNameScope(StyledElement styled) - { - _ = styled ?? throw new ArgumentNullException(nameof(styled)); - - return styled.GetValue(NameScopeProperty); - } - - /// - /// Sets the value of the attached on a styled element. - /// - /// The styled element. - /// The value to set. - public static void SetNameScope(StyledElement styled, INameScope value) - { - _ = styled ?? throw new ArgumentNullException(nameof(styled)); - - styled.SetValue(NameScopeProperty, value); - } - - /// - public void Register(string name, object element) - { - if (IsCompleted) - throw new InvalidOperationException("NameScope is completed, no further registrations are allowed"); - - _ = name ?? throw new ArgumentNullException(nameof(name)); - _ = element ?? throw new ArgumentNullException(nameof(element)); - - if (_inner.TryGetValue(name, out var existing)) - { - if (existing != element) - { - throw new ArgumentException($"Control with the name '{name}' already registered."); - } - } - else - { - _inner.Add(name, element); - if (_pendingSearches.TryGetValue(name, out var tcs)) - { - _pendingSearches.Remove(name); - tcs.SetResult(element); - } - } - } - - public SynchronousCompletionAsyncResult FindAsync(string name) - { - var found = Find(name); - if (found != null) - return new SynchronousCompletionAsyncResult(found); - if (IsCompleted) - return new SynchronousCompletionAsyncResult(null); - if (!_pendingSearches.TryGetValue(name, out var tcs)) - // We are intentionally running continuations synchronously here - _pendingSearches[name] = tcs = new SynchronousCompletionAsyncResultSource(); - - return tcs.AsyncResult; - } - - /// - public object? Find(string name) - { - _ = name ?? throw new ArgumentNullException(nameof(name)); - - _inner.TryGetValue(name, out var result); - return result; - } - - public void Complete() - { - IsCompleted = true; - foreach (var kp in _pendingSearches) - kp.Value.TrySetResult(null); - _pendingSearches.Clear(); - } - - - } -} diff --git a/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs b/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs deleted file mode 100644 index de41f5292c..0000000000 --- a/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs +++ /dev/null @@ -1,26 +0,0 @@ -#nullable enable -using System; - -namespace Avalonia.LogicalTree -{ - /// - /// Event args for event. - /// - public class ChildIndexChangedEventArgs : EventArgs - { - public ChildIndexChangedEventArgs() - { - } - - public ChildIndexChangedEventArgs(ILogical child) - { - Child = child; - } - - /// - /// Logical child which index was changed. - /// If null, all children should be reset. - /// - public ILogical? Child { get; } - } -} diff --git a/src/Avalonia.Styling/Properties/AssemblyInfo.cs b/src/Avalonia.Styling/Properties/AssemblyInfo.cs deleted file mode 100644 index ab034740ed..0000000000 --- a/src/Avalonia.Styling/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Runtime.CompilerServices; -using Avalonia.Metadata; - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.LogicalTree")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")] - -[assembly: InternalsVisibleTo("Avalonia.Styling.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj index ef200b5532..40ed4a0f87 100644 --- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj +++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj @@ -3,16 +3,10 @@ net6.0;netstandard2.0 - - - - - - - + diff --git a/src/Avalonia.Themes.Default/Controls/Button.xaml b/src/Avalonia.Themes.Default/Controls/Button.xaml index da36abe7ec..a2971c3ff6 100644 --- a/src/Avalonia.Themes.Default/Controls/Button.xaml +++ b/src/Avalonia.Themes.Default/Controls/Button.xaml @@ -18,7 +18,7 @@ Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" RecognizesAccessKey="True" - TextBlock.Foreground="{TemplateBinding Foreground}" + TextElement.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> diff --git a/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml b/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml index cf469eeac5..be6642467f 100644 --- a/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml +++ b/src/Avalonia.Themes.Default/Controls/CaptionButtons.xaml @@ -4,7 +4,7 @@ - + @@ -151,7 +151,7 @@ BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}" Content="{TemplateBinding Content}" - TextBlock.Foreground="{TemplateBinding Foreground}" + TextElement.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/> @@ -196,7 +196,7 @@ diff --git a/src/Avalonia.Themes.Default/Controls/DropDownButton.xaml b/src/Avalonia.Themes.Default/Controls/DropDownButton.xaml new file mode 100644 index 0000000000..662c6379fb --- /dev/null +++ b/src/Avalonia.Themes.Default/Controls/DropDownButton.xaml @@ -0,0 +1,67 @@ + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml b/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml index a9a03c8ed5..47398966f7 100644 --- a/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml +++ b/src/Avalonia.Themes.Default/Controls/RepeatButton.xaml @@ -24,7 +24,7 @@ ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" - TextBlock.Foreground="{TemplateBinding Foreground}" + TextElement.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> diff --git a/src/Avalonia.Themes.Default/Controls/SplitButton.xaml b/src/Avalonia.Themes.Default/Controls/SplitButton.xaml index ce20a1a165..0c46ce3724 100644 --- a/src/Avalonia.Themes.Default/Controls/SplitButton.xaml +++ b/src/Avalonia.Themes.Default/Controls/SplitButton.xaml @@ -16,6 +16,7 @@ 24 24 1 + 24 1 @@ -59,8 +60,11 @@ + + + @@ -146,7 +150,7 @@ SplitButton /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter"> - + diff --git a/src/Avalonia.Themes.Default/Controls/ToggleButton.xaml b/src/Avalonia.Themes.Default/Controls/ToggleButton.xaml index b14a239c35..17fb2af16c 100644 --- a/src/Avalonia.Themes.Default/Controls/ToggleButton.xaml +++ b/src/Avalonia.Themes.Default/Controls/ToggleButton.xaml @@ -18,7 +18,7 @@ Content="{TemplateBinding Content}" Padding="{TemplateBinding Padding}" RecognizesAccessKey="True" - TextBlock.Foreground="{TemplateBinding Foreground}" + TextElement.Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml index 846d45b839..468b723f5b 100644 --- a/src/Avalonia.Themes.Default/DefaultTheme.xaml +++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml @@ -14,6 +14,7 @@ + diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index 885661536c..35603fe216 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -3,16 +3,10 @@ net6.0;netstandard2.0 - - - - - - - + diff --git a/src/Avalonia.Themes.Fluent/Controls/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml index 533fabfb44..f545206a2f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Button.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Button.xaml @@ -4,7 +4,7 @@