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 @@
[](https://t.me/Avalonia)
[](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) []( https://aka.ms/dotnet-discord) [](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [](#backers) [](#sponsors) 
-[](https://www.nuget.org/packages/Avalonia) [](https://www.nuget.org/packages/Avalonia) [](https://www.myget.org/gallery/avalonia-ci) 
+[](https://www.nuget.org/packages/Avalonia) [](https://www.nuget.org/packages/Avalonia) 
## 📖 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
-
-
-
-
-
-
+
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 @@
-
+
@@ -19,9 +19,7 @@
-
-
-
+
@@ -45,40 +43,40 @@
-
+
-
+
-
-
diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml
index a9c8281cf0..e8e2df03b4 100644
--- a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml
@@ -61,17 +61,17 @@
diff --git a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml
index 12e148d2f9..649a186c7e 100644
--- a/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml
@@ -29,7 +29,7 @@
@@ -165,7 +165,7 @@
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
- TextBlock.Foreground="{TemplateBinding Foreground}"
+ Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
CornerRadius="{TemplateBinding CornerRadius}"/>
@@ -212,7 +212,7 @@
diff --git a/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml b/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml
new file mode 100644
index 0000000000..b96c689ab6
--- /dev/null
+++ b/src/Avalonia.Themes.Fluent/Controls/DropDownButton.xaml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 32
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml
index c5bc7b6a38..33d502772e 100644
--- a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml
@@ -110,7 +110,7 @@
BorderThickness="0"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
- TextBlock.Foreground="{DynamicResource ExpanderForeground}" />
+ Foreground="{DynamicResource ExpanderForeground}" />
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml
index e41e1b5fde..8011ed9daf 100644
--- a/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml
@@ -10,7 +10,7 @@
@@ -49,7 +49,7 @@
@@ -57,7 +57,7 @@
@@ -65,7 +65,7 @@
@@ -73,7 +73,7 @@
@@ -81,7 +81,7 @@
@@ -89,6 +89,6 @@
diff --git a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
index 831537f578..33cf6bfdde 100644
--- a/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml
@@ -218,7 +218,7 @@
diff --git a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml
index 5ee2dc527f..cd2c02c567 100644
--- a/src/Avalonia.Themes.Fluent/Controls/Slider.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml
@@ -45,7 +45,7 @@
-
@@ -107,7 +107,7 @@
-
@@ -208,7 +208,7 @@
diff --git a/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml
index 694e9ef579..59f68a1547 100644
--- a/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml
@@ -36,9 +36,9 @@
Content="{TemplateBinding Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
- TextBlock.FontFamily="{TemplateBinding FontFamily}"
- TextBlock.FontSize="{TemplateBinding FontSize}"
- TextBlock.FontWeight="{TemplateBinding FontWeight}" />
+ FontFamily="{TemplateBinding FontFamily}"
+ FontSize="{TemplateBinding FontSize}"
+ FontWeight="{TemplateBinding FontWeight}" />
@@ -69,7 +69,7 @@
diff --git a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml
index 9aa73fc52e..e03dc76f16 100644
--- a/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml
@@ -51,7 +51,7 @@
ContentTemplate="{TemplateBinding HeaderTemplate}"
Margin="{DynamicResource TimePickerTopHeaderMargin}"
MaxWidth="{DynamicResource TimePickerThemeMaxWidth}"
- TextBlock.Foreground="{DynamicResource TimePickerHeaderForeground}"
+ Foreground="{DynamicResource TimePickerHeaderForeground}"
HorizontalAlignment="Stretch"
VerticalAlignment="Top" />
@@ -77,7 +77,7 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Content="{TemplateBinding Content}"
- TextBlock.Foreground="{TemplateBinding Foreground}"
+ Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
@@ -139,7 +139,7 @@
diff --git a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml
index e66392113f..f86b67bb6c 100644
--- a/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml
@@ -107,7 +107,7 @@
@@ -116,7 +116,7 @@
@@ -125,7 +125,7 @@
@@ -134,7 +134,7 @@
@@ -143,7 +143,7 @@
@@ -152,7 +152,7 @@
@@ -161,7 +161,7 @@
diff --git a/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml b/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml
index 2ff5284d0d..8d14c2d972 100644
--- a/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml
@@ -9,7 +9,7 @@
-
+
diff --git a/src/Avalonia.Visuals/Animation/CompositePageTransition.cs b/src/Avalonia.Visuals/Animation/CompositePageTransition.cs
deleted file mode 100644
index 2a6eae3e38..0000000000
--- a/src/Avalonia.Visuals/Animation/CompositePageTransition.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Avalonia.Metadata;
-
-namespace Avalonia.Animation
-{
- ///
- /// Defines a composite page transition that can be used to combine multiple transitions.
- ///
- ///
- ///
- /// Instantiate the in XAML and initialize the
- /// property in order to have many animations triggered at once.
- /// For example, you can combine and .
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// ]]>
- ///
- ///
- ///
- public class CompositePageTransition : IPageTransition
- {
- ///
- /// Gets or sets the transitions to be executed. Can be defined from XAML.
- ///
- [Content]
- public List PageTransitions { get; set; } = new List();
-
- ///
- public Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
- {
- var transitionTasks = PageTransitions
- .Select(transition => transition.Start(from, to, forward, cancellationToken))
- .ToList();
- return Task.WhenAll(transitionTasks);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt
deleted file mode 100644
index b725993b44..0000000000
--- a/src/Avalonia.Visuals/ApiCompatBaseline.txt
+++ /dev/null
@@ -1,179 +0,0 @@
-Compat issues with assembly Avalonia.Visuals:
-MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.CompositePageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.CrossFade.Start(Avalonia.Visual, Avalonia.Visual)' does not exist in the implementation but it does exist in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' is present in the contract but not in the implementation.
-MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract.
-MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Media.FormattedText)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public System.Boolean Avalonia.Media.FontManager.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor(Avalonia.Platform.IPlatformRenderInterface)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.FormattedText.Bounds.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.FormattedText.Constraint.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Constraint.set(Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public System.Double Avalonia.Media.FormattedText.FontSize.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.FormattedText.FontSize.set(System.Double)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public System.Collections.Generic.IEnumerable Avalonia.Media.FormattedText.GetLines()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextHitTestResult Avalonia.Media.FormattedText.HitTestPoint(Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.FormattedText.HitTestTextPosition(System.Int32)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public System.Collections.Generic.IEnumerable Avalonia.Media.FormattedText.HitTestTextRange(System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Platform.IFormattedTextImpl Avalonia.Media.FormattedText.PlatformImpl.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.FormattedText.Spans.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Spans.set(System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public System.String Avalonia.Media.FormattedText.Text.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Text.set(System.String)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextWrapping Avalonia.Media.FormattedText.TextWrapping.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.FormattedText.TextWrapping.set(Avalonia.Media.TextWrapping)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FormattedText.Typeface.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Typeface.set(Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Media.FormattedTextLine' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Media.FormattedTextStyleSpan' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.GlyphRun..ctor()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.GlyphRun..ctor(Avalonia.Media.GlyphTypeface, System.Double, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, System.Int32)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphAdvances.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphAdvances.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphClusters.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphClusters.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphIndices.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphIndices.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphOffsets.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphOffsets.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphTypeface.set(Avalonia.Media.GlyphTypeface)' does not exist in the implementation but it does exist in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IBrush.Transform' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IBrush.Transform.get()' is present in the implementation but not in the contract.
-CannotSealType : Type 'Avalonia.Media.Pen' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
-MembersMustExist : Member 'protected void Avalonia.Media.Pen.AffectsRender(Avalonia.AvaloniaProperty[])' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'protected void Avalonia.Media.Pen.RaiseInvalidated(System.EventArgs)' does not exist in the implementation but it does exist in the contract.
-CannotSealType : Type 'Avalonia.Media.TextHitTestResult' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
-TypeCannotChangeClassification : Type 'Avalonia.Media.TextHitTestResult' is a 'struct' in the implementation but is a 'class' in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult..ctor()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsInside.set(System.Boolean)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsTrailing.set(System.Boolean)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.TextPosition.set(System.Int32)' does not exist in the implementation but it does exist in the contract.
-CannotMakeTypeAbstract : Type 'Avalonia.Media.TextTrimming' is abstract in the implementation but is not abstract in the contract.
-TypeCannotChangeClassification : Type 'Avalonia.Media.TextTrimming' is a 'class' in the implementation but is a 'struct' in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.CharacterEllipsis' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.None' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.WordEllipsis' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public System.Int32 System.Int32 Avalonia.Media.TextTrimming.value__' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.Typeface..ctor(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.Typeface..ctor(System.String, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableConicGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Double)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableLinearGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Nullable)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableRadialGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Nullable, System.Double)' does not exist in the implementation but it does exist in the contract.
-TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableSolidColorBrush..ctor(Avalonia.Media.Color, System.Double)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableTileBrush..ctor(Avalonia.Media.AlignmentX, Avalonia.Media.AlignmentY, Avalonia.RelativeRect, System.Double, Avalonia.RelativeRect, Avalonia.Media.Stretch, Avalonia.Media.TileMode, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract.
-CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract.
-CannotSealType : Type 'Avalonia.Media.TextFormatting.GenericTextParagraphProperties' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
-CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextFormatting.TextRunProperties Avalonia.Media.TextFormatting.GenericTextParagraphProperties.DefaultTextRunProperties' is non-virtual in the implementation but is virtual in the contract.
-CannotMakeMemberNonVirtual : Member 'public System.Double Avalonia.Media.TextFormatting.GenericTextParagraphProperties.LineHeight' is non-virtual in the implementation but is virtual in the contract.
-CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextAlignment Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextAlignment' is non-virtual in the implementation but is virtual in the contract.
-CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextWrapping Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextWrapping' is non-virtual in the implementation but is virtual in the contract.
-CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextFormatting.TextRunProperties Avalonia.Media.TextFormatting.GenericTextParagraphProperties.DefaultTextRunProperties.get()' is non-virtual in the implementation but is virtual in the contract.
-CannotMakeMemberNonVirtual : Member 'public System.Double Avalonia.Media.TextFormatting.GenericTextParagraphProperties.LineHeight.get()' is non-virtual in the implementation but is virtual in the contract.
-CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextAlignment Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextAlignment.get()' is non-virtual in the implementation but is virtual in the contract.
-CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextWrapping Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextWrapping.get()' is non-virtual in the implementation but is virtual in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.GenericTextRunProperties..ctor(Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextDecorationCollection, Avalonia.Media.IBrush, Avalonia.Media.IBrush, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapeableTextCharacters..ctor(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters..ctor(Avalonia.Media.GlyphRun, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult Avalonia.Media.TextFormatting.ShapedTextCharacters.Split(System.Int32)' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'protected System.Boolean Avalonia.Media.TextFormatting.TextCharacters.TryGetRunProperties(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, Avalonia.Media.Typeface, System.Int32)' does not exist in the implementation but it does exist in the contract.
-CannotAddAbstractMembers : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextCollapsingProperties.Collapse(Avalonia.Media.TextFormatting.TextLine)' is abstract in the implementation but is missing in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextCollapsingProperties.Style.get()' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Media.TextFormatting.TextCollapsingStyle' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextEndOfLine..ctor()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout..ctor(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.IBrush, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Media.TextTrimming, Avalonia.Media.TextDecorationCollection, System.Double, System.Double, System.Double, System.Int32, System.Collections.Generic.IReadOnlyList>)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.TextLayout.Size.get()' does not exist in the implementation but it does exist in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Baseline' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Extent' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextLine.HasOverflowed' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Height' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.NewLineLength' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangAfter' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangLeading' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangTrailing' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Start' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.TrailingWhitespaceLength' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Width' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.WidthIncludingTrailingWhitespace' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Baseline.get()' is abstract in the implementation but is missing in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract.
-CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Extent.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextLine.HasOverflowed.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Height.get()' is abstract in the implementation but is missing in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineMetrics Avalonia.Media.TextFormatting.TextLine.LineMetrics.get()' does not exist in the implementation but it does exist in the contract.
-CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.NewLineLength.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangAfter.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangLeading.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangTrailing.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Start.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.TrailingWhitespaceLength.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Width.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.WidthIncludingTrailingWhitespace.get()' is abstract in the implementation but is missing in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLineBreak..ctor(System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLineMetrics..ctor(Avalonia.Size, System.Double, Avalonia.Media.TextFormatting.TextRange, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineMetrics Avalonia.Media.TextFormatting.TextLineMetrics.Create(System.Collections.Generic.IEnumerable, Avalonia.Media.TextFormatting.TextRange, System.Double, Avalonia.Media.TextFormatting.TextParagraphProperties)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.TextLineMetrics.Size.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextRange Avalonia.Media.TextFormatting.TextLineMetrics.TextRange.get()' does not exist in the implementation but it does exist in the contract.
-CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextParagraphProperties.FirstLineInParagraph' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public Avalonia.Media.FlowDirection Avalonia.Media.TextFormatting.TextParagraphProperties.FlowDirection' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextParagraphProperties.Indent' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextParagraphProperties.FirstLineInParagraph.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public Avalonia.Media.FlowDirection Avalonia.Media.TextFormatting.TextParagraphProperties.FlowDirection.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextParagraphProperties.Indent.get()' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public Avalonia.Media.BaselineAlignment Avalonia.Media.TextFormatting.TextRunProperties.BaselineAlignment' is abstract in the implementation but is missing in the contract.
-CannotAddAbstractMembers : Member 'public Avalonia.Media.BaselineAlignment Avalonia.Media.TextFormatting.TextRunProperties.BaselineAlignment.get()' is abstract in the implementation but is missing in the contract.
-MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Media.TextFormatting.TextShaper.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract.
-CannotSealType : Type 'Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis..ctor(System.Double, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis.Style.get()' does not exist in the implementation but it does exist in the contract.
-CannotSealType : Type 'Avalonia.Media.TextFormatting.TextTrailingWordEllipsis' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
-MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextTrailingWordEllipsis..ctor(System.Double, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextTrailingWordEllipsis.Style.get()' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Media.TextFormatting.Unicode.BiDiClass' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public Avalonia.Media.TextFormatting.Unicode.BiDiClass Avalonia.Media.TextFormatting.Unicode.Codepoint.BiDiClass.get()' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'Avalonia.Platform.ExportRenderingSubsystemAttribute' does not exist in the implementation but it does exist in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawEllipse(Avalonia.Media.IBrush, Avalonia.Media.IPen, Avalonia.Rect)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Platform.IFormattedTextImpl)' is present in the contract but not in the implementation.
-MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Platform.IFormattedTextImpl)' does not exist in the implementation but it does exist in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.PopBitmapBlendMode()' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.PushBitmapBlendMode(Avalonia.Visuals.Media.Imaging.BitmapBlendingMode)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the contract but not in the implementation.
-MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontStretch, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract.
-TypesMustExist : Type 'Avalonia.Platform.IFormattedTextImpl' does not exist in the implementation but it does exist in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.Double Avalonia.Platform.IGeometryImpl.ContourLength' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.Double Avalonia.Platform.IGeometryImpl.ContourLength.get()' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IGeometryImpl.TryGetPointAndTangentAtDistance(System.Double, Avalonia.Point, Avalonia.Point)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IGeometryImpl.TryGetPointAtDistance(System.Double, Avalonia.Point)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IGeometryImpl.TryGetSegment(System.Double, System.Double, System.Boolean, Avalonia.Platform.IGeometryImpl)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGeometryImpl Avalonia.Platform.IPlatformRenderInterface.CreateCombinedGeometry(Avalonia.Media.GeometryCombineMode, Avalonia.Media.Geometry, Avalonia.Media.Geometry)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IFormattedTextImpl Avalonia.Platform.IPlatformRenderInterface.CreateFormattedText(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Size, System.Collections.Generic.IReadOnlyList)' is present in the contract but not in the implementation.
-MembersMustExist : Member 'public Avalonia.Platform.IFormattedTextImpl Avalonia.Platform.IPlatformRenderInterface.CreateFormattedText(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Size, System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGeometryImpl Avalonia.Platform.IPlatformRenderInterface.CreateGeometryGroup(Avalonia.Media.FillRule, System.Collections.Generic.IReadOnlyList)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' is present in the contract but not in the implementation.
-MembersMustExist : Member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' does not exist in the implementation but it does exist in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.IO.Stream)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.String)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToHeight(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.IPlatformSettings.TouchDoubleClickSize' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.IPlatformSettings.TouchDoubleClickSize.get()' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime.get()' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.TextFormatting.ShapedBuffer Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.GlyphTypeface, System.Double, System.Globalization.CultureInfo, System.SByte)' is present in the implementation but not in the contract.
-InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' is present in the contract but not in the implementation.
-MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'protected void Avalonia.Rendering.RendererBase.RenderFps(Avalonia.Platform.IDrawingContextImpl, Avalonia.Rect, System.Nullable)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'public void Avalonia.Utilities.ReadOnlySlice..ctor(System.ReadOnlyMemory, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 177
diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
deleted file mode 100644
index a0b1f99fa1..0000000000
--- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
- net6.0;netstandard2.0
- Avalonia
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs
deleted file mode 100644
index e974bbb100..0000000000
--- a/src/Avalonia.Visuals/Media/Color.cs
+++ /dev/null
@@ -1,319 +0,0 @@
-using System;
-using System.Globalization;
-#if !BUILDTASK
-using Avalonia.Animation.Animators;
-#endif
-
-namespace Avalonia.Media
-{
- ///
- /// An ARGB color.
- ///
-#if !BUILDTASK
- public
-#endif
- readonly struct Color : IEquatable
- {
- static Color()
- {
-#if !BUILDTASK
- Animation.Animation.RegisterAnimator(prop => typeof(Color).IsAssignableFrom(prop.PropertyType));
-#endif
- }
-
- ///
- /// Gets the Alpha component of the color.
- ///
- public byte A { get; }
-
- ///
- /// Gets the Red component of the color.
- ///
- public byte R { get; }
-
- ///
- /// Gets the Green component of the color.
- ///
- public byte G { get; }
-
- ///
- /// Gets the Blue component of the color.
- ///
- public byte B { get; }
-
- public Color(byte a, byte r, byte g, byte b)
- {
- A = a;
- R = r;
- G = g;
- B = b;
- }
-
- ///
- /// Creates a from alpha, red, green and blue components.
- ///
- /// The alpha component.
- /// The red component.
- /// The green component.
- /// The blue component.
- /// The color.
- public static Color FromArgb(byte a, byte r, byte g, byte b)
- {
- return new Color(a, r, g, b);
- }
-
- ///
- /// Creates a from red, green and blue components.
- ///
- /// The red component.
- /// The green component.
- /// The blue component.
- /// The color.
- public static Color FromRgb(byte r, byte g, byte b)
- {
- return new Color(0xff, r, g, b);
- }
-
- ///
- /// Creates a from an integer.
- ///
- /// The integer value.
- /// The color.
- public static Color FromUInt32(uint value)
- {
- return new Color(
- (byte)((value >> 24) & 0xff),
- (byte)((value >> 16) & 0xff),
- (byte)((value >> 8) & 0xff),
- (byte)(value & 0xff)
- );
- }
-
- ///
- /// Parses a color string.
- ///
- /// The color string.
- /// The .
- public static Color Parse(string s)
- {
- if (s is null)
- {
- throw new ArgumentNullException(nameof(s));
- }
-
- if (TryParse(s, out Color color))
- {
- return color;
- }
-
- throw new FormatException($"Invalid color string: '{s}'.");
- }
-
- ///
- /// Parses a color string.
- ///
- /// The color string.
- /// The .
- public static Color Parse(ReadOnlySpan s)
- {
- if (TryParse(s, out Color color))
- {
- return color;
- }
-
- throw new FormatException($"Invalid color string: '{s.ToString()}'.");
- }
-
- ///
- /// Parses a color string.
- ///
- /// The color string.
- /// The parsed color
- /// The status of the operation.
- public static bool TryParse(string s, out Color color)
- {
- color = default;
-
- if (s is null)
- {
- return false;
- }
-
- if (s.Length == 0)
- {
- return false;
- }
-
- if (s[0] == '#' && TryParseInternal(s.AsSpan(), out color))
- {
- return true;
- }
-
- var knownColor = KnownColors.GetKnownColor(s);
-
- if (knownColor != KnownColor.None)
- {
- color = knownColor.ToColor();
-
- return true;
- }
-
- return false;
- }
-
- ///
- /// Parses a color string.
- ///
- /// The color string.
- /// The parsed color
- /// The status of the operation.
- public static bool TryParse(ReadOnlySpan s, out Color color)
- {
- if (s.Length == 0)
- {
- color = default;
-
- return false;
- }
-
- if (s[0] == '#')
- {
- return TryParseInternal(s, out color);
- }
-
- var knownColor = KnownColors.GetKnownColor(s.ToString());
-
- if (knownColor != KnownColor.None)
- {
- color = knownColor.ToColor();
-
- return true;
- }
-
- color = default;
-
- return false;
- }
-
- private static bool TryParseInternal(ReadOnlySpan s, out Color color)
- {
- static bool TryParseCore(ReadOnlySpan input, ref Color color)
- {
- var alphaComponent = 0u;
-
- if (input.Length == 6)
- {
- alphaComponent = 0xff000000;
- }
- else if (input.Length != 8)
- {
- return false;
- }
-
- // TODO: (netstandard 2.1) Can use allocation free parsing.
- if (!uint.TryParse(input.ToString(), NumberStyles.HexNumber, CultureInfo.InvariantCulture,
- out var parsed))
- {
- return false;
- }
-
- color = FromUInt32(parsed | alphaComponent);
-
- return true;
- }
-
- color = default;
-
- ReadOnlySpan input = s.Slice(1);
-
- // Handle shorthand cases like #FFF (RGB) or #FFFF (ARGB).
- if (input.Length == 3 || input.Length == 4)
- {
- var extendedLength = 2 * input.Length;
-
-#if !BUILDTASK
- Span extended = stackalloc char[extendedLength];
-#else
- char[] extended = new char[extendedLength];
-#endif
-
- for (int i = 0; i < input.Length; i++)
- {
- extended[2 * i + 0] = input[i];
- extended[2 * i + 1] = input[i];
- }
-
- return TryParseCore(extended, ref color);
- }
-
- return TryParseCore(input, ref color);
- }
-
- ///
- /// Returns the string representation of the color.
- ///
- ///
- /// The string representation of the color.
- ///
- public override string ToString()
- {
- uint rgb = ToUint32();
- return KnownColors.GetKnownColorName(rgb) ?? $"#{rgb:x8}";
- }
-
- ///
- /// Returns the integer representation of the color.
- ///
- ///
- /// The integer representation of the color.
- ///
- public uint ToUint32()
- {
- return ((uint)A << 24) | ((uint)R << 16) | ((uint)G << 8) | (uint)B;
- }
-
- ///
- /// Returns the HSV color model equivalent of this RGB color.
- ///
- /// The HSV equivalent color.
- public HsvColor ToHsv()
- {
- // Use the by-channel conversion method directly for performance
- // Don't use the HsvColor(Color) constructor to avoid an extra HsvColor
- return HsvColor.FromRgb(R, G, B, A);
- }
-
- ///
- public bool Equals(Color other)
- {
- return A == other.A && R == other.R && G == other.G && B == other.B;
- }
-
- public override bool Equals(object? obj)
- {
- return obj is Color other && Equals(other);
- }
-
- public override int GetHashCode()
- {
- unchecked
- {
- int hashCode = A.GetHashCode();
- hashCode = (hashCode * 397) ^ R.GetHashCode();
- hashCode = (hashCode * 397) ^ G.GetHashCode();
- hashCode = (hashCode * 397) ^ B.GetHashCode();
- return hashCode;
- }
- }
-
- public static bool operator ==(Color left, Color right)
- {
- return left.Equals(right);
- }
-
- public static bool operator !=(Color left, Color right)
- {
- return !left.Equals(right);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Visuals/Media/DrawingContext.cs
deleted file mode 100644
index d0a73ca9a8..0000000000
--- a/src/Avalonia.Visuals/Media/DrawingContext.cs
+++ /dev/null
@@ -1,451 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Platform;
-using Avalonia.Rendering.SceneGraph;
-using Avalonia.Threading;
-using Avalonia.Utilities;
-using Avalonia.Visuals.Media.Imaging;
-
-namespace Avalonia.Media
-{
- public sealed class DrawingContext : IDisposable
- {
- private readonly bool _ownsImpl;
- private int _currentLevel;
-
-
- private static ThreadSafeObjectPool> StateStackPool { get; } =
- ThreadSafeObjectPool>.Default;
-
- private static ThreadSafeObjectPool> TransformStackPool { get; } =
- ThreadSafeObjectPool>.Default;
-
- private Stack? _states = StateStackPool.Get();
-
- private Stack? _transformContainers = TransformStackPool.Get();
-
- readonly struct TransformContainer
- {
- public readonly Matrix LocalTransform;
- public readonly Matrix ContainerTransform;
-
- public TransformContainer(Matrix localTransform, Matrix containerTransform)
- {
- LocalTransform = localTransform;
- ContainerTransform = containerTransform;
- }
- }
-
- public DrawingContext(IDrawingContextImpl impl)
- {
- PlatformImpl = impl;
- _ownsImpl = true;
- }
-
- public DrawingContext(IDrawingContextImpl impl, bool ownsImpl)
- {
- _ownsImpl = ownsImpl;
- PlatformImpl = impl;
- }
-
- public IDrawingContextImpl PlatformImpl { get; }
-
- private Matrix _currentTransform = Matrix.Identity;
-
- private Matrix _currentContainerTransform = Matrix.Identity;
-
- ///
- /// Gets the current transform of the drawing context.
- ///
- public Matrix CurrentTransform
- {
- get { return _currentTransform; }
- private set
- {
- _currentTransform = value;
- var transform = _currentTransform * _currentContainerTransform;
- PlatformImpl.Transform = transform;
- }
- }
-
- //HACK: This is a temporary hack that is used in the render loop
- //to update TransformedBounds property
- [Obsolete("HACK for render loop, don't use")]
- public Matrix CurrentContainerTransform => _currentContainerTransform;
-
- ///
- /// Draws an image.
- ///
- /// The image.
- /// The rect in the output to draw to.
- public void DrawImage(IImage source, Rect rect)
- {
- _ = source ?? throw new ArgumentNullException(nameof(source));
-
- DrawImage(source, new Rect(source.Size), rect);
- }
-
- ///
- /// Draws an image.
- ///
- /// The image.
- /// The rect in the image to draw.
- /// The rect in the output to draw to.
- /// The bitmap interpolation mode.
- public void DrawImage(IImage source, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = default)
- {
- _ = source ?? throw new ArgumentNullException(nameof(source));
-
- source.Draw(this, sourceRect, destRect, bitmapInterpolationMode);
- }
-
- ///
- /// Draws a line.
- ///
- /// The stroke pen.
- /// The first point of the line.
- /// The second point of the line.
- public void DrawLine(IPen pen, Point p1, Point p2)
- {
- if (PenIsVisible(pen))
- {
- PlatformImpl.DrawLine(pen, p1, p2);
- }
- }
-
- ///
- /// Draws a geometry.
- ///
- /// The fill brush.
- /// The stroke pen.
- /// The geometry.
- public void DrawGeometry(IBrush? brush, IPen? pen, Geometry geometry)
- {
- if (geometry.PlatformImpl is not null)
- DrawGeometry(brush, pen, geometry.PlatformImpl);
- }
-
- ///
- /// Draws a geometry.
- ///
- /// The fill brush.
- /// The stroke pen.
- /// The geometry.
- public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry)
- {
- _ = geometry ?? throw new ArgumentNullException(nameof(geometry));
-
- if (brush != null || PenIsVisible(pen))
- {
- PlatformImpl.DrawGeometry(brush, pen, geometry);
- }
- }
-
- ///
- /// Draws a rectangle with the specified Brush and Pen.
- ///
- /// The brush used to fill the rectangle, or null for no fill.
- /// The pen used to stroke the rectangle, or null for no stroke.
- /// The rectangle bounds.
- /// The radius in the X dimension of the rounded corners.
- /// This value will be clamped to the range of 0 to Width/2
- ///
- /// The radius in the Y dimension of the rounded corners.
- /// This value will be clamped to the range of 0 to Height/2
- ///
- /// Box shadow effect parameters
- ///
- /// The brush and the pen can both be null. If the brush is null, then no fill is performed.
- /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
- ///
- public void DrawRectangle(IBrush? brush, IPen? pen, Rect rect, double radiusX = 0, double radiusY = 0,
- BoxShadows boxShadows = default)
- {
- if (brush == null && !PenIsVisible(pen))
- {
- return;
- }
-
- if (!MathUtilities.IsZero(radiusX))
- {
- radiusX = Math.Min(radiusX, rect.Width / 2);
- }
-
- if (!MathUtilities.IsZero(radiusY))
- {
- radiusY = Math.Min(radiusY, rect.Height / 2);
- }
-
- PlatformImpl.DrawRectangle(brush, pen, new RoundedRect(rect, radiusX, radiusY), boxShadows);
- }
-
- ///
- /// Draws the outline of a rectangle.
- ///
- /// The pen.
- /// The rectangle bounds.
- /// The corner radius.
- public void DrawRectangle(IPen pen, Rect rect, float cornerRadius = 0.0f)
- {
- DrawRectangle(null, pen, rect, cornerRadius, cornerRadius);
- }
-
- ///
- /// Draws an ellipse with the specified Brush and Pen.
- ///
- /// The brush used to fill the ellipse, or null for no fill.
- /// The pen used to stroke the ellipse, or null for no stroke.
- /// The location of the center of the ellipse.
- /// The horizontal radius of the ellipse.
- /// The vertical radius of the ellipse.
- ///
- /// The brush and the pen can both be null. If the brush is null, then no fill is performed.
- /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
- ///
- public void DrawEllipse(IBrush? brush, IPen? pen, Point center, double radiusX, double radiusY)
- {
- if (brush == null && !PenIsVisible(pen))
- {
- return;
- }
-
- var originX = center.X - radiusX;
- var originY = center.Y - radiusY;
- var width = radiusX * 2;
- var height = radiusY * 2;
-
- PlatformImpl.DrawEllipse(brush, pen, new Rect(originX, originY, width, height));
- }
-
- ///
- /// Draws a custom drawing operation
- ///
- /// custom operation
- public void Custom(ICustomDrawOperation custom) => PlatformImpl.Custom(custom);
-
- ///
- /// Draws text.
- ///
- /// The upper-left corner of the text.
- /// The text.
- public void DrawText(FormattedText text, Point origin)
- {
- _ = text ?? throw new ArgumentNullException(nameof(text));
-
- text.Draw(this, origin);
- }
-
- ///
- /// Draws a glyph run.
- ///
- /// The foreground brush.
- /// The glyph run.
- public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
- {
- _ = glyphRun ?? throw new ArgumentNullException(nameof(glyphRun));
-
- if (foreground != null)
- {
- PlatformImpl.DrawGlyphRun(foreground, glyphRun);
- }
- }
-
- ///
- /// Draws a filled rectangle.
- ///
- /// The brush.
- /// The rectangle bounds.
- /// The corner radius.
- public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f)
- {
- DrawRectangle(brush, null, rect, cornerRadius, cornerRadius);
- }
-
- public readonly struct PushedState : IDisposable
- {
- private readonly int _level;
- private readonly DrawingContext _context;
- private readonly Matrix _matrix;
- private readonly PushedStateType _type;
-
- public enum PushedStateType
- {
- None,
- Matrix,
- Opacity,
- Clip,
- MatrixContainer,
- GeometryClip,
- OpacityMask,
- }
-
- public PushedState(DrawingContext context, PushedStateType type, Matrix matrix = default(Matrix))
- {
- if (context._states is null)
- throw new ObjectDisposedException(nameof(DrawingContext));
-
- _context = context;
- _type = type;
- _matrix = matrix;
- _level = context._currentLevel += 1;
- context._states.Push(this);
- }
-
- public void Dispose()
- {
- if (_type == PushedStateType.None)
- return;
- if (_context._states is null || _context._transformContainers is null)
- throw new ObjectDisposedException(nameof(DrawingContext));
- if (_context._currentLevel != _level)
- throw new InvalidOperationException("Wrong Push/Pop state order");
- _context._currentLevel--;
- _context._states.Pop();
- if (_type == PushedStateType.Matrix)
- _context.CurrentTransform = _matrix;
- else if (_type == PushedStateType.Clip)
- _context.PlatformImpl.PopClip();
- else if (_type == PushedStateType.Opacity)
- _context.PlatformImpl.PopOpacity();
- else if (_type == PushedStateType.GeometryClip)
- _context.PlatformImpl.PopGeometryClip();
- else if (_type == PushedStateType.OpacityMask)
- _context.PlatformImpl.PopOpacityMask();
- else if (_type == PushedStateType.MatrixContainer)
- {
- var cont = _context._transformContainers.Pop();
- _context._currentContainerTransform = cont.ContainerTransform;
- _context.CurrentTransform = cont.LocalTransform;
- }
- }
- }
-
-
- public PushedState PushClip(RoundedRect clip)
- {
- PlatformImpl.PushClip(clip);
- return new PushedState(this, PushedState.PushedStateType.Clip);
- }
-
- ///
- /// Pushes a clip rectangle.
- ///
- /// The clip rectangle.
- /// A disposable used to undo the clip rectangle.
- public PushedState PushClip(Rect clip)
- {
- PlatformImpl.PushClip(clip);
- return new PushedState(this, PushedState.PushedStateType.Clip);
- }
-
- ///
- /// Pushes a clip geometry.
- ///
- /// The clip geometry.
- /// A disposable used to undo the clip geometry.
- public PushedState PushGeometryClip(Geometry clip)
- {
- _ = clip ?? throw new ArgumentNullException(nameof(clip));
-
- // HACK: This check was added when nullable annotations pointed out that we're potentially
- // pushing a null value for the clip here. Ideally we'd return an empty PushedState here but
- // I don't want to make that change as part of adding nullable annotations.
- if (clip.PlatformImpl is null)
- throw new InvalidOperationException("Cannot push empty geometry clip.");
-
- PlatformImpl.PushGeometryClip(clip.PlatformImpl);
- return new PushedState(this, PushedState.PushedStateType.GeometryClip);
- }
-
- ///
- /// Pushes an opacity value.
- ///
- /// The opacity.
- /// A disposable used to undo the opacity.
- public PushedState PushOpacity(double opacity)
- //TODO: Eliminate platform-specific push opacity call
- {
- PlatformImpl.PushOpacity(opacity);
- return new PushedState(this, PushedState.PushedStateType.Opacity);
- }
-
- ///
- /// Pushes an opacity mask.
- ///
- /// The opacity mask.
- ///
- /// The size of the brush's target area. TODO: Are we sure this is needed?
- ///
- /// A disposable to undo the opacity mask.
- public PushedState PushOpacityMask(IBrush mask, Rect bounds)
- {
- PlatformImpl.PushOpacityMask(mask, bounds);
- return new PushedState(this, PushedState.PushedStateType.OpacityMask);
- }
-
- ///
- /// Pushes a matrix post-transformation.
- ///
- /// The matrix
- /// A disposable used to undo the transformation.
- public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform * matrix);
-
- ///
- /// Pushes a matrix pre-transformation.
- ///
- /// The matrix
- /// A disposable used to undo the transformation.
- public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix * CurrentTransform);
-
- ///
- /// Sets the current matrix transformation.
- ///
- /// The matrix
- /// A disposable used to undo the transformation.
- public PushedState PushSetTransform(Matrix matrix)
- {
- var oldMatrix = CurrentTransform;
- CurrentTransform = matrix;
-
- return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix);
- }
-
- ///
- /// Pushes a new transform context.
- ///
- /// A disposable used to undo the transformation.
- public PushedState PushTransformContainer()
- {
- if (_transformContainers is null)
- throw new ObjectDisposedException(nameof(DrawingContext));
- _transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));
- _currentContainerTransform = CurrentTransform * _currentContainerTransform;
- _currentTransform = Matrix.Identity;
- return new PushedState(this, PushedState.PushedStateType.MatrixContainer);
- }
-
- ///
- /// Disposes of any resources held by the .
- ///
- public void Dispose()
- {
- if (_states is null || _transformContainers is null)
- throw new ObjectDisposedException(nameof(DrawingContext));
- while (_states.Count != 0)
- _states.Peek().Dispose();
- StateStackPool.Return(_states);
- _states = null;
- if (_transformContainers.Count != 0)
- throw new InvalidOperationException("Transform container stack is non-empty");
- TransformStackPool.Return(_transformContainers);
- _transformContainers = null;
- if (_ownsImpl)
- PlatformImpl.Dispose();
- }
-
- private static bool PenIsVisible(IPen? pen)
- {
- return pen?.Brush != null && pen.Thickness > 0;
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/DrawingImage.cs b/src/Avalonia.Visuals/Media/DrawingImage.cs
deleted file mode 100644
index 488822b693..0000000000
--- a/src/Avalonia.Visuals/Media/DrawingImage.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using System;
-using Avalonia.Data;
-using Avalonia.Metadata;
-using Avalonia.Platform;
-using Avalonia.Visuals.Media.Imaging;
-
-namespace Avalonia.Media
-{
- ///
- /// An that uses a for content.
- ///
- public class DrawingImage : AvaloniaObject, IImage, IAffectsRender
- {
- public DrawingImage()
- {
- }
-
- public DrawingImage(Drawing drawing)
- {
- Drawing = drawing;
- }
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty DrawingProperty =
- AvaloniaProperty.Register(nameof(Drawing));
-
- ///
- public event EventHandler? Invalidated;
-
- ///
- /// Gets or sets the drawing content.
- ///
- [Content]
- public Drawing Drawing
- {
- get => GetValue(DrawingProperty);
- set => SetValue(DrawingProperty, value);
- }
-
- ///
- public Size Size => Drawing?.GetBounds().Size ?? default;
-
- ///
- void IImage.Draw(
- DrawingContext context,
- Rect sourceRect,
- Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode)
- {
- var drawing = Drawing;
-
- if (drawing == null)
- {
- return;
- }
-
- var bounds = drawing.GetBounds();
- var scale = Matrix.CreateScale(
- destRect.Width / sourceRect.Width,
- destRect.Height / sourceRect.Height);
- var translate = Matrix.CreateTranslation(
- -sourceRect.X + destRect.X - bounds.X,
- -sourceRect.Y + destRect.Y - bounds.Y);
-
- using (context.PushClip(destRect))
- using (context.PushPreTransform(translate * scale))
- {
- Drawing?.Draw(context);
- }
- }
-
- ///
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
- {
- base.OnPropertyChanged(change);
-
- if (change.Property == DrawingProperty)
- {
- RaiseInvalidated(EventArgs.Empty);
- }
- }
-
- ///
- /// Raises the event.
- ///
- /// The event args.
- protected void RaiseInvalidated(EventArgs e) => Invalidated?.Invoke(this, e);
- }
-}
diff --git a/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs b/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs
deleted file mode 100644
index cdea5f5fa3..0000000000
--- a/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs
+++ /dev/null
@@ -1,345 +0,0 @@
-using System;
-
-namespace Avalonia.Media
-{
- public class ExperimentalAcrylicMaterial : AvaloniaObject, IMutableExperimentalAcrylicMaterial
- {
- private Color _effectiveTintColor;
- private Color _effectiveLuminosityColor;
-
- static ExperimentalAcrylicMaterial()
- {
- AffectsRender(
- TintColorProperty,
- BackgroundSourceProperty,
- TintOpacityProperty,
- MaterialOpacityProperty,
- PlatformTransparencyCompensationLevelProperty);
-
- TintColorProperty.Changed.AddClassHandler((b, e) =>
- {
- b._effectiveTintColor = GetEffectiveTintColor(b.TintColor, b.TintOpacity);
- b._effectiveLuminosityColor = b.GetEffectiveLuminosityColor();
- });
-
- TintOpacityProperty.Changed.AddClassHandler((b, e) =>
- {
- b._effectiveTintColor = GetEffectiveTintColor(b.TintColor, b.TintOpacity);
- b._effectiveLuminosityColor = b.GetEffectiveLuminosityColor();
- });
-
- MaterialOpacityProperty.Changed.AddClassHandler((b, e) =>
- {
- b._effectiveTintColor = GetEffectiveTintColor(b.TintColor, b.TintOpacity);
- b._effectiveLuminosityColor = b.GetEffectiveLuminosityColor();
- });
-
- PlatformTransparencyCompensationLevelProperty.Changed.AddClassHandler((b, e) =>
- {
- b._effectiveTintColor = GetEffectiveTintColor(b.TintColor, b.TintOpacity);
- b._effectiveLuminosityColor = b.GetEffectiveLuminosityColor();
- });
- }
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty TintColorProperty =
- AvaloniaProperty.Register(nameof(TintColor));
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty BackgroundSourceProperty =
- AvaloniaProperty.Register(nameof(BackgroundSource));
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty TintOpacityProperty =
- AvaloniaProperty.Register(nameof(TintOpacity), 0.8);
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty MaterialOpacityProperty =
- AvaloniaProperty.Register(nameof(MaterialOpacity), 0.5);
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty PlatformTransparencyCompensationLevelProperty =
- AvaloniaProperty.Register(nameof(PlatformTransparencyCompensationLevel), 0.0);
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty FallbackColorProperty =
- AvaloniaProperty.Register(nameof(FallbackColor));
-
- ///
- public event EventHandler? Invalidated;
-
- ///
- /// Gets or Sets the BackgroundSource .
- ///
- public AcrylicBackgroundSource BackgroundSource
- {
- get => GetValue(BackgroundSourceProperty);
- set => SetValue(BackgroundSourceProperty, value);
- }
-
- ///
- /// Gets or Sets the TintColor.
- ///
- public Color TintColor
- {
- get => GetValue(TintColorProperty);
- set => SetValue(TintColorProperty, value);
- }
-
- ///
- /// Gets or Sets the Tint Opacity.
- ///
- public double TintOpacity
- {
- get => GetValue(TintOpacityProperty);
- set => SetValue(TintOpacityProperty, value);
- }
-
- ///
- /// Gets or Sets the Fallback Color.
- /// This is used on rendering plaforms that dont support acrylic.
- ///
- public Color FallbackColor
- {
- get => GetValue(FallbackColorProperty);
- set => SetValue(FallbackColorProperty, value);
- }
-
- ///
- /// Gets or Sets the MaterialOpacity.
- /// This makes the material more or less opaque.
- ///
- public double MaterialOpacity
- {
- get => GetValue(MaterialOpacityProperty);
- set => SetValue(MaterialOpacityProperty, value);
- }
-
- ///
- /// Gets or Sets the PlatformTransparencyCompensationLevel.
- /// This value defines the minimum that can be used.
- /// It means material opacity is re-scaled from this value to 1.
- ///
- public double PlatformTransparencyCompensationLevel
- {
- get => GetValue(PlatformTransparencyCompensationLevelProperty);
- set => SetValue(PlatformTransparencyCompensationLevelProperty, value);
- }
-
- Color IExperimentalAcrylicMaterial.MaterialColor => _effectiveLuminosityColor;
-
- Color IExperimentalAcrylicMaterial.TintColor => _effectiveTintColor;
-
- private struct HsvColor
- {
- public float Hue { get; set; }
- public float Saturation { get; set; }
- public float Value { get; set; }
- }
-
- private static HsvColor RgbToHsv(Color color)
- {
- var r = color.R / 255.0f;
- var g = color.G / 255.0f;
- var b = color.B / 255.0f;
- var max = Math.Max(r, Math.Max(g, b));
- var min = Math.Min(r, Math.Min(g, b));
-
- float h, s, v;
- h = v = max;
-
- var d = max - min;
- s = max == 0 ? 0 : d / max;
-
- if (max == min)
- {
- h = 0; // achromatic
- }
- else
- {
- if (max == r)
- {
- h = (g - b) / d + (g < b ? 6 : 0);
- }
- else if (max == g)
- {
- h = (b - r) / d + 2;
- }
- else if (max == b)
- {
- h = (r - g) / d + 4;
- }
-
- h /= 6;
- }
-
- return new HsvColor { Hue = h, Saturation = s, Value = v };
- }
-
- private static Color GetEffectiveTintColor(Color tintColor, double tintOpacity)
- {
- // Update tintColor's alpha with the combined opacity value
- double tintOpacityModifier = GetTintOpacityModifier(tintColor);
-
- return new Color((byte)(255 * ((255.0 / tintColor.A) * tintOpacity) * tintOpacityModifier), tintColor.R, tintColor.G, tintColor.B);
- }
-
- private static double GetTintOpacityModifier(Color tintColor)
- {
- // This method supresses the maximum allowable tint opacity depending on the luminosity and saturation of a color by
- // compressing the range of allowable values - for example, a user-defined value of 100% will be mapped to 45% for pure
- // white (100% luminosity), 85% for pure black (0% luminosity), and 90% for pure gray (50% luminosity). The intensity of
- // the effect increases linearly as luminosity deviates from 50%. After this effect is calculated, we cancel it out
- // linearly as saturation increases from zero.
-
- const double midPoint = 0.5; // Mid point of HsvV range that these calculations are based on. This is here for easy tuning.
-
- const double whiteMaxOpacity = 0.2; // 100% luminosity
- const double midPointMaxOpacity = 0.45; // 50% luminosity
- const double blackMaxOpacity = 0.45; // 0% luminosity
-
- var hsv = RgbToHsv(tintColor);
-
- double opacityModifier = midPointMaxOpacity;
-
- if (hsv.Value != midPoint)
- {
- // Determine maximum suppression amount
- double lowestMaxOpacity = midPointMaxOpacity;
- double maxDeviation = midPoint;
-
- if (hsv.Value > midPoint)
- {
- lowestMaxOpacity = whiteMaxOpacity; // At white (100% hsvV)
- maxDeviation = 1 - maxDeviation;
- }
- else if (hsv.Value < midPoint)
- {
- lowestMaxOpacity = blackMaxOpacity; // At black (0% hsvV)
- }
-
- double maxOpacitySuppression = midPointMaxOpacity - lowestMaxOpacity;
-
- // Determine normalized deviation from the midpoint
- double deviation = Math.Abs(hsv.Value - midPoint);
- double normalizedDeviation = deviation / maxDeviation;
-
- // If we have saturation, reduce opacity suppression to allow that color to come through more
- if (hsv.Saturation > 0)
- {
- // Dampen opacity suppression based on how much saturation there is
- maxOpacitySuppression *= Math.Max(1 - (hsv.Saturation * 2), 0.0);
- }
-
- double opacitySuppression = maxOpacitySuppression * normalizedDeviation;
-
- opacityModifier = midPointMaxOpacity - opacitySuppression;
- }
-
- return opacityModifier;
- }
-
- private Color GetEffectiveLuminosityColor()
- {
- double? luminosityOpacity = MaterialOpacity;
-
- return GetLuminosityColor(luminosityOpacity);
- }
-
- private static byte Trim(double value)
- {
- value = Math.Min(Math.Floor(value * 256), 255);
-
- if (value < 0)
- {
- return 0;
- }
- else if (value > 255)
- {
- return 255;
- }
-
- return (byte)value;
- }
-
- private static float RGBMax(Color color)
- {
- if (color.R > color.G)
- return (color.R > color.B) ? color.R : color.B;
- else
- return (color.G > color.B) ? color.G : color.B;
- }
-
- private static float RGBMin(Color color)
- {
- if (color.R < color.G)
- return (color.R < color.B) ? color.R : color.B;
- else
- return (color.G < color.B) ? color.G : color.B;
- }
-
- // The tintColor passed into this method should be the original, unmodified color created using user values for TintColor + TintOpacity
- private Color GetLuminosityColor(double? luminosityOpacity)
- {
- // Calculate the HSL lightness value of the color.
- var max = (float)RGBMax(TintColor) / 255.0f;
- var min = (float)RGBMin(TintColor) / 255.0f;
-
- var lightness = (max + min) / 2.0;
-
- lightness = 1 - ((1 - lightness) * (luminosityOpacity ?? 1));
-
- lightness = 0.13 + (lightness * 0.74);
-
- var luminosityColor = new Color(255, Trim(lightness), Trim(lightness), Trim(lightness));
-
- var compensationMultiplier = 1 - PlatformTransparencyCompensationLevel;
- return new Color((byte)(255 * Math.Max(Math.Min(PlatformTransparencyCompensationLevel + ((luminosityOpacity ?? 1) * compensationMultiplier), 1.0), 0.0)), luminosityColor.R, luminosityColor.G, luminosityColor.B);
- }
-
- ///
- /// Marks a property as affecting the brush's visual representation.
- ///
- /// The properties.
- ///
- /// After a call to this method in a brush's static constructor, any change to the
- /// property will cause the event to be raised on the brush.
- ///
- protected static void AffectsRender(params AvaloniaProperty[] properties)
- where T : ExperimentalAcrylicMaterial
- {
- static void Invalidate(AvaloniaPropertyChangedEventArgs e)
- {
- (e.Sender as T)?.RaiseInvalidated(EventArgs.Empty);
- }
-
- foreach (var property in properties)
- {
- property.Changed.Subscribe(e => Invalidate(e));
- }
- }
-
- ///
- /// Raises the event.
- ///
- /// The event args.
- protected void RaiseInvalidated(EventArgs e) => Invalidated?.Invoke(this, e);
-
- public IExperimentalAcrylicMaterial ToImmutable()
- {
- return new ImmutableExperimentalAcrylicMaterial(this);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/FormattedText.cs b/src/Avalonia.Visuals/Media/FormattedText.cs
deleted file mode 100644
index 1cac3243e3..0000000000
--- a/src/Avalonia.Visuals/Media/FormattedText.cs
+++ /dev/null
@@ -1,1400 +0,0 @@
-using System;
-using System.Collections;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Globalization;
-using Avalonia.Media.TextFormatting;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media
-{
- ///
- /// The FormattedText class is targeted at programmers needing to add some simple text to a MIL visual.
- ///
- public class FormattedText
- {
- public const double DefaultRealToIdeal = 28800.0 / 96;
- public const double DefaultIdealToReal = 1 / DefaultRealToIdeal;
- public const int IdealInfiniteWidth = 0x3FFFFFFE;
- public const double RealInfiniteWidth = IdealInfiniteWidth * DefaultIdealToReal;
-
- public const double GreatestMultiplierOfEm = 100;
-
- private const double MaxFontEmSize = RealInfiniteWidth / GreatestMultiplierOfEm;
-
- // properties and format runs
- private ReadOnlySlice _text;
- private readonly SpanVector _formatRuns = new SpanVector(null);
- private SpanPosition _latestPosition;
-
- private GenericTextParagraphProperties _defaultParaProps;
-
- private double _maxTextWidth = double.PositiveInfinity;
- private double[]? _maxTextWidths;
- private double _maxTextHeight = double.PositiveInfinity;
- private int _maxLineCount = int.MaxValue;
- private TextTrimming _trimming = TextTrimming.WordEllipsis;
-
- // text source callbacks
- private TextSourceImplementation? _textSourceImpl;
-
- // cached metrics
- private CachedMetrics? _metrics;
-
- ///
- /// Construct a FormattedText object.
- ///
- /// String of text to be displayed.
- /// Culture of text.
- /// Flow direction of text.
- /// Type face used to display text.
- /// Font em size in visual units (1/96 of an inch).
- /// Foreground brush used to render text.
- public FormattedText(
- string textToFormat,
- CultureInfo culture,
- FlowDirection flowDirection,
- Typeface typeface,
- double emSize,
- IBrush foreground)
- {
- if (culture is null)
- {
- throw new ArgumentNullException(nameof(culture));
- }
-
- ValidateFlowDirection(flowDirection, nameof(flowDirection));
-
- ValidateFontSize(emSize);
-
- _text = textToFormat != null ?
- new ReadOnlySlice(textToFormat.AsMemory()) :
- throw new ArgumentNullException(nameof(textToFormat));
-
- var runProps = new GenericTextRunProperties(
- typeface,
- emSize,
- null, // decorations
- foreground,
- null, // highlight background
- BaselineAlignment.Baseline,
- culture
- );
-
- _latestPosition = _formatRuns.SetValue(0, _text.Length, runProps, _latestPosition);
-
- _defaultParaProps = new GenericTextParagraphProperties(
- flowDirection,
- TextAlignment.Left,
- false,
- false,
- runProps,
- TextWrapping.WrapWithOverflow,
- 0, // line height not specified
- 0 // indentation not specified
- );
-
- InvalidateMetrics();
- }
-
- private static void ValidateFontSize(double emSize)
- {
- if (emSize <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(emSize), "The parameter value must be greater than zero.");
- }
-
- if (emSize > MaxFontEmSize)
- {
- throw new ArgumentOutOfRangeException(nameof(emSize), $"The parameter value cannot be greater than '{MaxFontEmSize}'");
- }
-
- if (double.IsNaN(emSize))
- {
- throw new ArgumentOutOfRangeException(nameof(emSize), "The parameter value must be a number.");
- }
- }
-
- private static void ValidateFlowDirection(FlowDirection flowDirection, string parameterName)
- {
- if ((int)flowDirection < 0 || (int)flowDirection > (int)FlowDirection.RightToLeft)
- {
- throw new InvalidEnumArgumentException(parameterName, (int)flowDirection, typeof(FlowDirection));
- }
- }
-
- private int ValidateRange(int startIndex, int count)
- {
- if (startIndex < 0 || startIndex > _text.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(startIndex));
- }
-
- var limit = startIndex + count;
-
- if (count < 0 || limit < startIndex || limit > _text.Length)
- {
- throw new ArgumentOutOfRangeException(nameof(count));
- }
-
- return limit;
- }
-
- private void InvalidateMetrics()
- {
- _metrics = null;
- }
-
- ///
- /// Sets foreground brush used for drawing text
- ///
- /// Foreground brush
- public void SetForegroundBrush(IBrush foregroundBrush)
- {
- SetForegroundBrush(foregroundBrush, 0, _text.Length);
- }
-
- ///
- /// Sets foreground brush used for drawing text
- ///
- /// Foreground brush
- /// The start index of initial character to apply the change to.
- /// The number of characters the change should be applied to.
- public void SetForegroundBrush(IBrush foregroundBrush, int startIndex, int count)
- {
- var limit = ValidateRange(startIndex, count);
- for (var i = startIndex; i < limit;)
- {
- var formatRider = new SpanRider(_formatRuns, _latestPosition, i);
- i = Math.Min(limit, i + formatRider.Length);
-
-#pragma warning disable 6506
- // Presharp warns that runProps is not validated, but it can never be null
- // because the rider is already checked to be in range
-
- if (!(formatRider.CurrentElement is GenericTextRunProperties runProps))
- {
- throw new NotSupportedException($"{nameof(runProps)} can not be null.");
- }
-
- if (runProps.ForegroundBrush == foregroundBrush)
- {
- continue;
- }
-
- var newProps = new GenericTextRunProperties(
- runProps.Typeface,
- runProps.FontRenderingEmSize,
- runProps.TextDecorations,
- foregroundBrush,
- runProps.BackgroundBrush,
- runProps.BaselineAlignment,
- runProps.CultureInfo
- );
-
-#pragma warning restore 6506
- _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition,
- newProps, formatRider.SpanPosition);
- }
- }
-
- ///
- /// Sets or changes the font family for the text object
- ///
- /// Font family name
- public void SetFontFamily(string fontFamily)
- {
- SetFontFamily(fontFamily, 0, _text.Length);
- }
-
- ///
- /// Sets or changes the font family for the text object
- ///
- /// Font family name
- /// The start index of initial character to apply the change to.
- /// The number of characters the change should be applied to.
- public void SetFontFamily(string fontFamily, int startIndex, int count)
- {
- if (fontFamily == null)
- {
- throw new ArgumentNullException(nameof(fontFamily));
- }
-
- SetFontFamily(new FontFamily(fontFamily), startIndex, count);
- }
-
- ///
- /// Sets or changes the font family for the text object
- ///
- /// Font family
- public void SetFontFamily(FontFamily fontFamily)
- {
- SetFontFamily(fontFamily, 0, _text.Length);
- }
-
- ///
- /// Sets or changes the font family for the text object
- ///
- /// Font family
- /// The start index of initial character to apply the change to.
- /// The number of characters the change should be applied to.
- public void SetFontFamily(FontFamily fontFamily, int startIndex, int count)
- {
- if (fontFamily == null)
- {
- throw new ArgumentNullException(nameof(fontFamily));
- }
-
- var limit = ValidateRange(startIndex, count);
-
- for (var i = startIndex; i < limit;)
- {
- var formatRider = new SpanRider(_formatRuns, _latestPosition, i);
-
- i = Math.Min(limit, i + formatRider.Length);
-
-#pragma warning disable 6506
- // Presharp warns that runProps is not validated, but it can never be null
- // because the rider is already checked to be in range
-
- if (!(formatRider.CurrentElement is GenericTextRunProperties runProps))
- {
- throw new NotSupportedException($"{nameof(runProps)} can not be null.");
- }
-
- var oldTypeface = runProps.Typeface;
-
- if (fontFamily.Equals(oldTypeface.FontFamily))
- {
- continue;
- }
-
- var newProps = new GenericTextRunProperties(
- new Typeface(fontFamily, oldTypeface.Style, oldTypeface.Weight),
- runProps.FontRenderingEmSize,
- runProps.TextDecorations,
- runProps.ForegroundBrush,
- runProps.BackgroundBrush,
- runProps.BaselineAlignment,
- runProps.CultureInfo
- );
-
-#pragma warning restore 6506
- _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition,
- newProps, formatRider.SpanPosition);
-
- InvalidateMetrics();
- }
- }
-
-
- ///
- /// Sets or changes the font em size measured in MIL units
- ///
- /// Font em size
- public void SetFontSize(double emSize)
- {
- SetFontSize(emSize, 0, _text.Length);
- }
-
- ///
- /// Sets or changes the font em size measured in MIL units
- ///
- /// Font em size
- /// The start index of initial character to apply the change to.
- /// The number of characters the change should be applied to.
- public void SetFontSize(double emSize, int startIndex, int count)
- {
- ValidateFontSize(emSize);
-
- var limit = ValidateRange(startIndex, count);
- for (var i = startIndex; i < limit;)
- {
- var formatRider = new SpanRider(_formatRuns, _latestPosition, i);
-
- i = Math.Min(limit, i + formatRider.Length);
-
-#pragma warning disable 6506
- // Presharp warns that runProps is not validated, but it can never be null
- // because the rider is already checked to be in range
-
- if (!(formatRider.CurrentElement is GenericTextRunProperties runProps))
- {
- throw new NotSupportedException($"{nameof(runProps)} can not be null.");
- }
-
- if (runProps.FontRenderingEmSize == emSize)
- {
- continue;
- }
-
- var newProps = new GenericTextRunProperties(
- runProps.Typeface,
- emSize,
- runProps.TextDecorations,
- runProps.ForegroundBrush,
- runProps.BackgroundBrush,
- runProps.BaselineAlignment,
- runProps.CultureInfo
- );
-
- _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition,
- newProps, formatRider.SpanPosition);
-
-#pragma warning restore 6506
- InvalidateMetrics();
- }
- }
-
- ///
- /// Sets or changes the culture for the text object.
- ///
- /// The new culture for the text object.
- public void SetCulture(CultureInfo culture)
- {
- SetCulture(culture, 0, _text.Length);
- }
-
- ///
- /// Sets or changes the culture for the text object.
- ///
- /// The new culture for the text object.
- /// The start index of initial character to apply the change to.
- /// The number of characters the change should be applied to.
- public void SetCulture(CultureInfo culture, int startIndex, int count)
- {
- if (culture is null)
- {
- throw new ArgumentNullException(nameof(culture));
- }
-
- var limit = ValidateRange(startIndex, count);
-
- for (var i = startIndex; i < limit;)
- {
- var formatRider = new SpanRider(_formatRuns, _latestPosition, i);
-
- i = Math.Min(limit, i + formatRider.Length);
-
-#pragma warning disable 6506
- // Presharp warns that runProps is not validated, but it can never be null
- // because the rider is already checked to be in range
-
- if (!(formatRider.CurrentElement is GenericTextRunProperties runProps))
- {
- throw new NotSupportedException($"{nameof(runProps)} can not be null.");
- }
-
- if (runProps.CultureInfo == culture)
- {
- continue;
- }
-
- var newProps = new GenericTextRunProperties(
- runProps.Typeface,
- runProps.FontRenderingEmSize,
- runProps.TextDecorations,
- runProps.ForegroundBrush,
- runProps.BackgroundBrush,
- runProps.BaselineAlignment,
- culture
- );
-
-#pragma warning restore 6506
- _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition,
- newProps, formatRider.SpanPosition);
-
- InvalidateMetrics();
- }
- }
-
- ///
- /// Sets or changes the font weight
- ///
- /// Font weight
- public void SetFontWeight(FontWeight weight)
- {
- SetFontWeight(weight, 0, _text.Length);
- }
-
- ///
- /// Sets or changes the font weight
- ///
- /// Font weight
- /// The start index of initial character to apply the change to.
- /// The number of characters the change should be applied to.
- public void SetFontWeight(FontWeight weight, int startIndex, int count)
- {
- var limit = ValidateRange(startIndex, count);
-
- for (var i = startIndex; i < limit;)
- {
- var formatRider = new SpanRider(_formatRuns, _latestPosition, i);
-
- i = Math.Min(limit, i + formatRider.Length);
-
-#pragma warning disable 6506
- // Presharp warns that runProps is not validated, but it can never be null
- // because the rider is already checked to be in range
-
- if (!(formatRider.CurrentElement is GenericTextRunProperties runProps))
- {
- throw new NotSupportedException($"{nameof(runProps)} can not be null.");
- }
-
- var oldTypeface = runProps.Typeface;
-
- if (oldTypeface.Weight == weight)
- {
- continue;
- }
-
- var newProps = new GenericTextRunProperties(
- new Typeface(oldTypeface.FontFamily, oldTypeface.Style, weight),
- runProps.FontRenderingEmSize,
- runProps.TextDecorations,
- runProps.ForegroundBrush,
- runProps.BackgroundBrush,
- runProps.BaselineAlignment,
- runProps.CultureInfo
- );
-#pragma warning restore 6506
- _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition, newProps, formatRider.SpanPosition);
-
- InvalidateMetrics();
- }
- }
-
- ///
- /// Sets or changes the font style
- ///
- /// Font style
- public void SetFontStyle(FontStyle style)
- {
- SetFontStyle(style, 0, _text.Length);
- }
-
- ///
- /// Sets or changes the font style
- ///
- /// Font style
- /// The start index of initial character to apply the change to.
- /// The number of characters the change should be applied to.
- public void SetFontStyle(FontStyle style, int startIndex, int count)
- {
- var limit = ValidateRange(startIndex, count);
- for (var i = startIndex; i < limit;)
- {
- var formatRider = new SpanRider(_formatRuns, _latestPosition, i);
-
- i = Math.Min(limit, i + formatRider.Length);
-
-#pragma warning disable 6506
- // Presharp warns that runProps is not validated, but it can never be null
- // because the rider is already checked to be in range
-
- if (!(formatRider.CurrentElement is GenericTextRunProperties runProps))
- {
- throw new NotSupportedException($"{nameof(runProps)} can not be null.");
- }
-
- var oldTypeface = runProps.Typeface;
-
- if (oldTypeface.Style == style)
- {
- continue;
- }
-
- var newProps = new GenericTextRunProperties(
- new Typeface(oldTypeface.FontFamily, style, oldTypeface.Weight),
- runProps.FontRenderingEmSize,
- runProps.TextDecorations,
- runProps.ForegroundBrush,
- runProps.BackgroundBrush,
- runProps.BaselineAlignment,
- runProps.CultureInfo
- );
-#pragma warning restore 6506
-
- _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition, newProps, formatRider.SpanPosition);
-
- InvalidateMetrics(); // invalidate cached metrics
- }
- }
-
- ///
- /// Sets or changes the type face
- ///
- /// Typeface
- public void SetFontTypeface(Typeface typeface)
- {
- SetFontTypeface(typeface, 0, _text.Length);
- }
-
- ///
- /// Sets or changes the type face
- ///
- /// Typeface
- /// The start index of initial character to apply the change to.
- /// The number of characters the change should be applied to.
- public void SetFontTypeface(Typeface typeface, int startIndex, int count)
- {
- var limit = ValidateRange(startIndex, count);
-
- for (var i = startIndex; i < limit;)
- {
- var formatRider = new SpanRider(_formatRuns, _latestPosition, i);
-
- i = Math.Min(limit, i + formatRider.Length);
-
-#pragma warning disable 6506
- // Presharp warns that runProps is not validated, but it can never be null
- // because the rider is already checked to be in range
-
- if (!(formatRider.CurrentElement is GenericTextRunProperties runProps))
- {
- throw new NotSupportedException($"{nameof(runProps)} can not be null.");
- }
-
- if (runProps.Typeface == typeface)
- {
- continue;
- }
-
- var newProps = new GenericTextRunProperties(
- typeface,
- runProps.FontRenderingEmSize,
- runProps.TextDecorations,
- runProps.ForegroundBrush,
- runProps.BackgroundBrush,
- runProps.BaselineAlignment,
- runProps.CultureInfo
- );
-#pragma warning restore 6506
-
- _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition,
- newProps, formatRider.SpanPosition);
-
- InvalidateMetrics();
- }
- }
-
- ///
- /// Sets or changes the text decorations
- ///
- /// Text decorations
- public void SetTextDecorations(TextDecorationCollection textDecorations)
- {
- SetTextDecorations(textDecorations, 0, _text.Length);
- }
-
- ///
- /// Sets or changes the text decorations
- ///
- /// Text decorations
- /// The start index of initial character to apply the change to.
- /// The number of characters the change should be applied to.
- public void SetTextDecorations(TextDecorationCollection textDecorations, int startIndex, int count)
- {
- var limit = ValidateRange(startIndex, count);
-
- for (var i = startIndex; i < limit;)
- {
- var formatRider = new SpanRider(_formatRuns, _latestPosition, i);
-
- i = Math.Min(limit, i + formatRider.Length);
-
-#pragma warning disable 6506
- // Presharp warns that runProps is not validated, but it can never be null
- // because the rider is already checked to be in range
-
- if (!(formatRider.CurrentElement is GenericTextRunProperties runProps))
- {
- throw new NotSupportedException($"{nameof(runProps)} can not be null.");
- }
-
- if (runProps.TextDecorations == textDecorations)
- {
- continue;
- }
-
- var newProps = new GenericTextRunProperties(
- runProps.Typeface,
- runProps.FontRenderingEmSize,
- textDecorations,
- runProps.ForegroundBrush,
- runProps.BackgroundBrush,
- runProps.BaselineAlignment,
- runProps.CultureInfo
- );
-#pragma warning restore 6506
-
- _latestPosition = _formatRuns.SetValue(formatRider.CurrentPosition, i - formatRider.CurrentPosition,
- newProps, formatRider.SpanPosition);
- }
- }
-
- /// Note: enumeration is temporarily made private
- /// because of PS #828532
- ///
- ///
- /// Strongly typed enumerator used for enumerating text lines
- ///
- private struct LineEnumerator : IEnumerator, IDisposable
- {
- private int _lineCount;
- private double _totalHeight;
- private TextLine? _nextLine;
- private readonly TextFormatter _formatter;
- private readonly FormattedText _that;
- private readonly ITextSource _textSource;
-
- // these are needed because _currentLine can be disposed before the next MoveNext() call
- private double _previousHeight;
-
- // line break before _currentLine, needed in case we have to reformat it with collapsing symbol
- private TextLineBreak? _previousLineBreak;
-
- internal LineEnumerator(FormattedText text)
- {
- _previousHeight = 0;
- Length = 0;
- _previousLineBreak = null;
-
- Position = 0;
- _lineCount = 0;
- _totalHeight = 0;
- Current = null;
- _nextLine = null;
- _formatter = TextFormatter.Current;
- _that = text;
- _textSource = _that._textSourceImpl ??= new TextSourceImplementation(_that);
- }
-
- public void Dispose()
- {
- Current = null;
-
- _nextLine = null;
- }
-
- private int Position { get; set; }
-
- private int Length { get; set; }
-
- ///
- /// Gets the current text line in the collection
- ///
- public TextLine? Current { get; private set; }
-
- ///
- /// Gets the current text line in the collection
- ///
- object? IEnumerator.Current => Current;
-
- ///
- /// Gets the paragraph width used to format the current text line
- ///
- internal double CurrentParagraphWidth
- {
- get
- {
- return MaxLineLength(_lineCount);
- }
- }
-
- private double MaxLineLength(int line)
- {
- if (_that._maxTextWidths == null)
- return _that._maxTextWidth;
- return _that._maxTextWidths[Math.Min(line, _that._maxTextWidths.Length - 1)];
- }
-
- ///
- /// Advances the enumerator to the next text line of the collection
- ///
- /// true if the enumerator was successfully advanced to the next element;
- /// false if the enumerator has passed the end of the collection
- public bool MoveNext()
- {
- if (Current == null)
- { // this is the first line
- if (_that._text.Length == 0)
- {
- return false;
- }
-
- Current = FormatLine(
- _textSource,
- Position,
- MaxLineLength(_lineCount),
- _that._defaultParaProps!,
- null // no previous line break
- );
-
- // check if this line fits the text height
- if (_totalHeight + Current.Height > _that._maxTextHeight)
- {
- Current = null;
-
- return false;
- }
- Debug.Assert(_nextLine == null);
- }
- else
- {
- // there is no next line or it didn't fit
- // either way we're finished
- if (_nextLine == null)
- {
- return false;
- }
-
- _totalHeight += _previousHeight;
- Position += Length;
- ++_lineCount;
-
- Current = _nextLine;
- _nextLine = null;
- }
-
- var currentLineBreak = Current.TextLineBreak;
-
- // this line is guaranteed to fit the text height
- Debug.Assert(_totalHeight + Current.Height <= _that._maxTextHeight);
-
- // now, check if the next line fits, we need to do this on this iteration
- // because we might need to add ellipsis to the current line
- // as a result of the next line measurement
-
- // maybe there is no next line at all
- if (Position + Current.TextRange.Length < _that._text.Length)
- {
- bool nextLineFits;
-
- if (_lineCount + 1 >= _that._maxLineCount)
- {
- nextLineFits = false;
- }
- else
- {
- _nextLine = FormatLine(
- _textSource,
- Position + Current.TextRange.Length,
- MaxLineLength(_lineCount + 1),
- _that._defaultParaProps,
- currentLineBreak
- );
-
- nextLineFits = (_totalHeight + Current.Height + _nextLine.Height <= _that._maxTextHeight);
- }
-
- if (!nextLineFits)
- {
- _nextLine = null;
-
- if (_that._trimming != TextTrimming.None && !Current.HasCollapsed)
- {
- // recreate the current line with ellipsis added
- // Note: Paragraph ellipsis is not supported today. We'll workaround
- // it here by faking a non-wrap text on finite column width.
- var currentWrap = _that._defaultParaProps!.TextWrapping;
-
- _that._defaultParaProps.SetTextWrapping(TextWrapping.NoWrap);
-
- Current = FormatLine(
- _that._textSourceImpl!,
- Position,
- MaxLineLength(_lineCount),
- _that._defaultParaProps,
- _previousLineBreak
- );
-
- currentLineBreak = Current.TextLineBreak;
-
- _that._defaultParaProps.SetTextWrapping(currentWrap);
- }
- }
- }
-
- _previousHeight = Current.Height;
-
- Length = Current.TextRange.Length;
-
- _previousLineBreak = currentLineBreak;
-
- return true;
- }
-
- ///
- /// Wrapper of TextFormatter.FormatLine that auto-collapses the line if needed.
- ///
- private TextLine FormatLine(ITextSource textSource, int textSourcePosition, double maxLineLength, TextParagraphProperties paraProps, TextLineBreak? lineBreak)
- {
- var line = _formatter.FormatLine(
- textSource,
- textSourcePosition,
- maxLineLength,
- paraProps,
- lineBreak
- );
-
- if (_that._trimming != TextTrimming.None && line.HasOverflowed && line.TextRange.Length > 0)
- {
- // what I really need here is the last displayed text run of the line
- // textSourcePosition + line.Length - 1 works except the end of paragraph case,
- // where line length includes the fake paragraph break run
- Debug.Assert(_that._text.Length > 0 && textSourcePosition + line.TextRange.Length <= _that._text.Length + 1);
-
- var thatFormatRider = new SpanRider(
- _that._formatRuns,
- _that._latestPosition,
- Math.Min(textSourcePosition + line.TextRange.Length - 1, _that._text.Length - 1)
- );
-
- var lastRunProps = (GenericTextRunProperties)thatFormatRider.CurrentElement!;
-
- TextCollapsingProperties collapsingProperties = _that._trimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(maxLineLength, lastRunProps));
-
- var collapsedLine = line.Collapse(collapsingProperties);
-
- line = collapsedLine;
- }
- return line;
- }
-
-
- ///
- /// Sets the enumerator to its initial position,
- /// which is before the first element in the collection
- ///
- public void Reset()
- {
- Position = 0;
- _lineCount = 0;
- _totalHeight = 0;
- Current = null;
- _nextLine = null;
- }
- }
-
- ///
- /// Returns an enumerator that can iterate through the text line collection
- ///
- private LineEnumerator GetEnumerator()
- {
- return new LineEnumerator(this);
- }
-#if NEVER
- ///
- /// Returns an enumerator that can iterate through the text line collection
- ///
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-#endif
-
- private void AdvanceLineOrigin(ref Point lineOrigin, TextLine currentLine)
- {
- var height = currentLine.Height;
-
- // advance line origin according to the flow direction
- switch (_defaultParaProps.FlowDirection)
- {
- case FlowDirection.LeftToRight:
- case FlowDirection.RightToLeft:
- lineOrigin = lineOrigin.WithY(lineOrigin.Y + height);
- break;
- }
- }
-
- private class CachedMetrics
- {
- // vertical
- public double Height;
- public double Baseline;
-
- // horizontal
- public double Width;
- public double WidthIncludingTrailingWhitespace;
-
- // vertical bounding box metrics
- public double Extent;
- public double OverhangAfter;
-
- // horizontal bounding box metrics
- public double OverhangLeading;
- public double OverhangTrailing;
- }
-
- ///
- /// Defines the flow direction
- ///
- public FlowDirection FlowDirection
- {
- set
- {
- ValidateFlowDirection(value, "value");
- _defaultParaProps.SetFlowDirection(value);
- InvalidateMetrics();
- }
- get
- {
- return _defaultParaProps.FlowDirection;
- }
- }
-
- ///
- /// Defines the alignment of text within the column
- ///
- public TextAlignment TextAlignment
- {
- set
- {
- _defaultParaProps.SetTextAlignment(value);
- InvalidateMetrics();
- }
- get
- {
- return _defaultParaProps.TextAlignment;
- }
- }
-
- ///
- /// Gets or sets the height of, or the spacing between, each line where
- /// zero represents the default line height.
- ///
- public double LineHeight
- {
- set
- {
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value), "Parameter must be greater than or equal to zero.");
- }
-
- _defaultParaProps.SetLineHeight(value);
-
- InvalidateMetrics();
- }
- get
- {
- return _defaultParaProps.LineHeight;
- }
- }
-
- ///
- /// The MaxTextWidth property defines the alignment edges for the FormattedText.
- /// For example, left aligned text is wrapped such that the leftmost glyph alignment point
- /// on each line falls exactly on the left edge of the rectangle.
- /// Note that for many fonts, especially in italic style, some glyph strokes may extend beyond the edges of the alignment rectangle.
- /// For this reason, it is recommended that clients draw text with at least 1/6 em (i.e of the font size) unused margin space either side.
- /// Zero value of MaxTextWidth is equivalent to the maximum possible paragraph width.
- ///
- public double MaxTextWidth
- {
- set
- {
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value), "Parameter must be greater than or equal to zero.");
- }
-
- _maxTextWidth = value;
-
- InvalidateMetrics();
- }
- get
- {
- return _maxTextWidth;
- }
- }
-
- ///
- /// Sets the array of lengths,
- /// which will be applied to each line of text in turn.
- /// If the text covers more lines than there are entries in the length array,
- /// the last entry is reused as many times as required.
- /// The maxTextWidths array overrides the MaxTextWidth property.
- ///
- /// The max text width array
- public void SetMaxTextWidths(double[] maxTextWidths)
- {
- if (maxTextWidths == null || maxTextWidths.Length <= 0)
- {
- throw new ArgumentNullException(nameof(maxTextWidths));
- }
-
- _maxTextWidths = maxTextWidths;
-
- InvalidateMetrics();
- }
-
- ///
- /// Obtains a copy of the array of lengths,
- /// which will be applied to each line of text in turn.
- /// If the text covers more lines than there are entries in the length array,
- /// the last entry is reused as many times as required.
- /// The maxTextWidths array overrides the MaxTextWidth property.
- ///
- /// The copy of max text width array
- public double[] GetMaxTextWidths()
- {
- return _maxTextWidths != null ? (double[])_maxTextWidths.Clone() : Array.Empty();
- }
-
- ///
- /// Sets the maximum length of a column of text.
- /// The last line of text displayed is the last whole line that will fit within this limit,
- /// or the nth line as specified by MaxLineCount, whichever occurs first.
- /// Use the Trimming property to control how the omission of text is indicated.
- ///
- public double MaxTextHeight
- {
- set
- {
- if (value <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value), $"'{nameof(MaxTextHeight)}' property value must be greater than zero.");
- }
-
- if (double.IsNaN(value))
- {
- throw new ArgumentOutOfRangeException(nameof(value), $"'{nameof(MaxTextHeight)}' property value cannot be NaN.");
- }
-
- _maxTextHeight = value;
-
- InvalidateMetrics();
- }
- get
- {
- return _maxTextHeight;
- }
- }
-
- ///
- /// Defines the maximum number of lines to display.
- /// The last line of text displayed is the lineCount-1'th line,
- /// or the last whole line that will fit within the count set by MaxTextHeight,
- /// whichever occurs first.
- /// Use the Trimming property to control how the omission of text is indicated
- ///
- public int MaxLineCount
- {
- set
- {
- if (value <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value), "The parameter value must be greater than zero.");
- }
-
- _maxLineCount = value;
-
- InvalidateMetrics();
- }
- get
- {
- return _maxLineCount;
- }
- }
-
- ///
- /// Defines how omission of text is indicated.
- /// CharacterEllipsis trimming allows partial words to be displayed,
- /// while WordEllipsis removes whole words to fit.
- /// Both guarantee to include an ellipsis ('...') at the end of the lines
- /// where text has been trimmed as a result of line and column limits.
- ///
- public TextTrimming Trimming
- {
- set
- {
- _trimming = value;
-
- _defaultParaProps.SetTextWrapping(_trimming == TextTrimming.None ?
- TextWrapping.Wrap :
- TextWrapping.WrapWithOverflow);
-
- InvalidateMetrics();
- }
- get
- {
- return _trimming;
- }
- }
-
- ///
- /// Lazily initializes the cached metrics EXCEPT for black box metrics and
- /// returns the CachedMetrics structure.
- ///
- private CachedMetrics Metrics
- {
- get
- {
- return _metrics ??= DrawAndCalculateMetrics(
- null, // drawing context
- new Point(), // drawing offset
- false);
- }
- }
-
- ///
- /// Lazily initializes the cached metrics INCLUDING black box metrics and
- /// returns the CachedMetrics structure.
- ///
- private CachedMetrics BlackBoxMetrics
- {
- get
- {
- if (_metrics == null || double.IsNaN(_metrics.Extent))
- {
- // We need to obtain the metrics, including black box metrics.
-
- _metrics = DrawAndCalculateMetrics(
- null, // drawing context
- new Point(), // drawing offset
- true); // calculate black box metrics
- }
- return _metrics;
- }
- }
-
- ///
- /// The distance from the top of the first line to the bottom of the last line.
- ///
- public double Height
- {
- get
- {
- return Metrics.Height;
- }
- }
-
- ///
- /// The distance from the topmost black pixel of the first line
- /// to the bottommost black pixel of the last line.
- ///
- public double Extent
- {
- get
- {
- return BlackBoxMetrics.Extent;
- }
- }
-
- ///
- /// The distance from the top of the first line to the baseline of the first line.
- ///
- public double Baseline
- {
- get
- {
- return Metrics.Baseline;
- }
- }
-
- ///
- /// The distance from the bottom of the last line to the extent bottom.
- ///
- public double OverhangAfter
- {
- get
- {
- return BlackBoxMetrics.OverhangAfter;
- }
- }
-
- ///
- /// The maximum distance from the leading black pixel to the leading alignment point of a line.
- ///
- public double OverhangLeading
- {
- get
- {
- return BlackBoxMetrics.OverhangLeading;
- }
- }
-
- ///
- /// The maximum distance from the trailing black pixel to the trailing alignment point of a line.
- ///
- public double OverhangTrailing
- {
- get
- {
- return BlackBoxMetrics.OverhangTrailing;
- }
- }
-
- ///
- /// The maximum advance width between the leading and trailing alignment points of a line,
- /// excluding the width of whitespace characters at the end of the line.
- ///
- public double Width
- {
- get
- {
- return Metrics.Width;
- }
- }
-
- ///
- /// The maximum advance width between the leading and trailing alignment points of a line,
- /// including the width of whitespace characters at the end of the line.
- ///
- public double WidthIncludingTrailingWhitespace
- {
- get
- {
- return Metrics.WidthIncludingTrailingWhitespace;
- }
- }
-
- ///
- /// Draws the text object
- ///
- internal void Draw(DrawingContext drawingContext, Point origin)
- {
- var lineOrigin = origin;
-
- if (_metrics != null && !double.IsNaN(_metrics.Extent))
- {
- // we can't use foreach because it requires GetEnumerator and associated classes to be public
- // foreach (TextLine currentLine in this)
- using (var enumerator = GetEnumerator())
- {
- while (enumerator.MoveNext())
- {
- var currentLine = enumerator.Current!;
-
- currentLine.Draw(drawingContext, lineOrigin);
-
- AdvanceLineOrigin(ref lineOrigin, currentLine);
- }
- }
- }
- else
- {
- // Calculate metrics as we draw to avoid formatting again if we need metrics later; we compute
- // black box metrics too because these are already known as a side-effect of drawing
-
- _metrics = DrawAndCalculateMetrics(drawingContext, origin, true);
- }
- }
-
- private CachedMetrics DrawAndCalculateMetrics(DrawingContext? drawingContext, Point drawingOffset, bool getBlackBoxMetrics)
- {
- var metrics = new CachedMetrics();
-
- if (_text.Length == 0)
- {
- return metrics;
- }
-
- // we can't use foreach because it requires GetEnumerator and associated classes to be public
- // foreach (TextLine currentLine in this)
-
- using (var enumerator = GetEnumerator())
- {
- var first = true;
-
- double accBlackBoxLeft, accBlackBoxTop, accBlackBoxRight, accBlackBoxBottom;
- accBlackBoxLeft = accBlackBoxTop = double.MaxValue;
- accBlackBoxRight = accBlackBoxBottom = double.MinValue;
-
- var origin = new Point(0, 0);
-
- // Holds the TextLine.Start of the longest line. Thus it will hold the minimum value
- // of TextLine.Start among all the lines that forms the text. The overhangs (leading and trailing)
- // are calculated with an offset as a result of the same issue with TextLine.Start.
- // So, we compute this offset and remove it later from the values of the overhangs.
- var lineStartOfLongestLine = double.MaxValue;
-
- while (enumerator.MoveNext())
- {
- // enumerator will dispose the currentLine
- var currentLine = enumerator.Current!;
-
- // if we're drawing, do it first as this will compute black box metrics as a side-effect
- if (drawingContext != null)
- {
- currentLine.Draw(drawingContext,
- new Point(origin.X + drawingOffset.X, origin.Y + drawingOffset.Y));
- }
-
- if (getBlackBoxMetrics)
- {
- var blackBoxLeft = origin.X + currentLine.Start + currentLine.OverhangLeading;
- var blackBoxRight = origin.X + currentLine.Start + currentLine.Width - currentLine.OverhangTrailing;
- var blackBoxBottom = origin.Y + currentLine.Height + currentLine.OverhangAfter;
- var blackBoxTop = blackBoxBottom - currentLine.Extent;
-
- accBlackBoxLeft = Math.Min(accBlackBoxLeft, blackBoxLeft);
- accBlackBoxRight = Math.Max(accBlackBoxRight, blackBoxRight);
- accBlackBoxBottom = Math.Max(accBlackBoxBottom, blackBoxBottom);
- accBlackBoxTop = Math.Min(accBlackBoxTop, blackBoxTop);
-
- metrics.OverhangAfter = currentLine.OverhangAfter;
- }
-
- metrics.Height += currentLine.Height;
- metrics.Width = Math.Max(metrics.Width, currentLine.Width);
- metrics.WidthIncludingTrailingWhitespace = Math.Max(metrics.WidthIncludingTrailingWhitespace, currentLine.WidthIncludingTrailingWhitespace);
- lineStartOfLongestLine = Math.Min(lineStartOfLongestLine, currentLine.Start);
-
- if (first)
- {
- metrics.Baseline = currentLine.Baseline;
- first = false;
- }
-
- AdvanceLineOrigin(ref origin, currentLine);
- }
-
- if (getBlackBoxMetrics)
- {
- metrics.Extent = accBlackBoxBottom - accBlackBoxTop;
- metrics.OverhangLeading = accBlackBoxLeft - lineStartOfLongestLine;
- metrics.OverhangTrailing = metrics.Width - (accBlackBoxRight - lineStartOfLongestLine);
- }
- else
- {
- // indicate that black box metrics are not known
- metrics.Extent = double.NaN;
- }
- }
-
- return metrics;
- }
-
- private class TextSourceImplementation : ITextSource
- {
- private readonly FormattedText _that;
-
- public TextSourceImplementation(FormattedText text)
- {
- _that = text;
- }
-
- ///
- public TextRun? GetTextRun(int textSourceCharacterIndex)
- {
- if (textSourceCharacterIndex >= _that._text.Length)
- {
- return null;
- }
-
- var thatFormatRider = new SpanRider(_that._formatRuns, _that._latestPosition, textSourceCharacterIndex);
-
- TextRunProperties properties = (GenericTextRunProperties)thatFormatRider.CurrentElement!;
-
- var textCharacters = new TextCharacters(_that._text, textSourceCharacterIndex, thatFormatRider.Length,
- properties);
-
- return textCharacters;
- }
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/GradientStops.cs b/src/Avalonia.Visuals/Media/GradientStops.cs
deleted file mode 100644
index efc11bacd6..0000000000
--- a/src/Avalonia.Visuals/Media/GradientStops.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.Collections;
-using Avalonia.Media.Immutable;
-
-namespace Avalonia.Media
-{
- ///
- /// A collection of s.
- ///
- public class GradientStops : AvaloniaList
- {
- public GradientStops()
- {
- ResetBehavior = ResetBehavior.Remove;
- }
-
- public IReadOnlyList ToImmutable()
- {
- return this.Select(x => new ImmutableGradientStop(x.Offset, x.Color)).ToList();
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs
deleted file mode 100644
index 8b2f10d088..0000000000
--- a/src/Avalonia.Visuals/Media/HsvColor.cs
+++ /dev/null
@@ -1,478 +0,0 @@
-// Color conversion portions of this source file are adapted from the WinUI project.
-// (https://github.com/microsoft/microsoft-ui-xaml)
-//
-// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
-
-using System;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media
-{
- ///
- /// Defines a color using the hue/saturation/value (HSV) model.
- ///
-#if !BUILDTASK
- public
-#endif
- readonly struct HsvColor : IEquatable
- {
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The Alpha (transparency) channel value in the range from 0..1.
- /// The Hue channel value in the range from 0..360.
- /// The Saturation channel value in the range from 0..1.
- /// The Value channel value in the range from 0..1.
- public HsvColor(
- double alpha,
- double hue,
- double saturation,
- double value)
- {
- A = MathUtilities.Clamp(alpha, 0.0, 1.0);
- H = MathUtilities.Clamp(hue, 0.0, 360.0);
- S = MathUtilities.Clamp(saturation, 0.0, 1.0);
- V = MathUtilities.Clamp(value, 0.0, 1.0);
- }
-
- ///
- /// Initializes a new instance of the struct.
- ///
- ///
- /// This constructor exists only for internal use where performance is critical.
- /// Whether or not the channel values are in the correct ranges must be known.
- ///
- /// The Alpha (transparency) channel value in the range from 0..1.
- /// The Hue channel value in the range from 0..360.
- /// The Saturation channel value in the range from 0..1.
- /// The Value channel value in the range from 0..1.
- /// Whether to clamp channel values to their required ranges.
- internal HsvColor(
- double alpha,
- double hue,
- double saturation,
- double value,
- bool clampValues)
- {
- if (clampValues)
- {
- A = MathUtilities.Clamp(alpha, 0.0, 1.0);
- H = MathUtilities.Clamp(hue, 0.0, 360.0);
- S = MathUtilities.Clamp(saturation, 0.0, 1.0);
- V = MathUtilities.Clamp(value, 0.0, 1.0);
- }
- else
- {
- A = alpha;
- H = hue;
- S = saturation;
- V = value;
- }
- }
-
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The RGB color to convert to HSV.
- public HsvColor(Color color)
- {
- var hsv = HsvColor.FromRgb(color);
-
- A = hsv.A;
- H = hsv.H;
- S = hsv.S;
- V = hsv.V;
- }
-
- ///
- /// Gets the Alpha (transparency) channel value in the range from 0..1.
- ///
- public double A { get; }
-
- ///
- /// Gets the Hue channel value in the range from 0..360.
- ///
- public double H { get; }
-
- ///
- /// Gets the Saturation channel value in the range from 0..1.
- ///
- public double S { get; }
-
- ///
- /// Gets the Value channel value in the range from 0..1.
- ///
- public double V { get; }
-
- ///
- public bool Equals(HsvColor other)
- {
- return other.A == A &&
- other.H == H &&
- other.S == S &&
- other.V == V;
- }
-
- ///
- public override bool Equals(object? obj)
- {
- if (obj is HsvColor hsvColor)
- {
- return Equals(hsvColor);
- }
- else
- {
- return false;
- }
- }
-
- ///
- /// Gets a hashcode for this object.
- /// Hashcode is not guaranteed to be unique.
- ///
- /// The hashcode for this object.
- public override int GetHashCode()
- {
- // Same algorithm as Color
- // This is used instead of HashCode.Combine() due to .NET Standard 2.0 requirements
- unchecked
- {
- int hashCode = A.GetHashCode();
- hashCode = (hashCode * 397) ^ H.GetHashCode();
- hashCode = (hashCode * 397) ^ S.GetHashCode();
- hashCode = (hashCode * 397) ^ V.GetHashCode();
- return hashCode;
- }
- }
-
- ///
- /// Returns the RGB color model equivalent of this HSV color.
- ///
- /// The RGB equivalent color.
- public Color ToRgb()
- {
- // Use the by-channel conversion method directly for performance
- return HsvColor.ToRgb(H, S, V, A);
- }
-
- ///
- /// Creates a new from individual color channel values.
- ///
- ///
- /// This exists for symmetry with the struct; however, the
- /// appropriate constructor should commonly be used instead.
- ///
- /// The Alpha (transparency) channel value in the range from 0..1.
- /// The Hue channel value in the range from 0..360.
- /// The Saturation channel value in the range from 0..1.
- /// The Value channel value in the range from 0..1.
- /// A new built from the individual color channel values.
- public static HsvColor FromAhsv(double a, double h, double s, double v)
- {
- return new HsvColor(a, h, s, v);
- }
-
- ///
- /// Converts the given HSV color to it's RGB color equivalent.
- ///
- /// The color in the HSV color model.
- /// A new RGB equivalent to the given HSVA values.
- public static Color ToRgb(HsvColor hsvColor)
- {
- return HsvColor.ToRgb(hsvColor.H, hsvColor.S, hsvColor.V, hsvColor.A);
- }
-
- ///
- /// Converts the given HSVA color channel values to it's RGB color equivalent.
- ///
- /// The hue channel value in the HSV color model in the range from 0..360.
- /// The saturation channel value in the HSV color model in the range from 0..1.
- /// The value channel value in the HSV color model in the range from 0..1.
- /// The alpha channel value in the range from 0..1.
- /// A new RGB equivalent to the given HSVA values.
- public static Color ToRgb(
- double hue,
- double saturation,
- double value,
- double alpha = 1.0)
- {
- // Note: Conversion code is originally based on the C++ in WinUI (licensed MIT)
- // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/Common/ColorConversion.cpp
- // This was used because it is the best documented and likely most optimized for performance
- // Alpha channel support was added
-
- // We want the hue to be between 0 and 359,
- // so we first ensure that that's the case.
- while (hue >= 360.0)
- {
- hue -= 360.0;
- }
-
- while (hue < 0.0)
- {
- hue += 360.0;
- }
-
- // We similarly clamp saturation, value and alpha between 0 and 1.
- saturation = saturation < 0.0 ? 0.0 : saturation;
- saturation = saturation > 1.0 ? 1.0 : saturation;
-
- value = value < 0.0 ? 0.0 : value;
- value = value > 1.0 ? 1.0 : value;
-
- alpha = alpha < 0.0 ? 0.0 : alpha;
- alpha = alpha > 1.0 ? 1.0 : alpha;
-
- // The first thing that we need to do is to determine the chroma (see above for its definition).
- // Remember from above that:
- //
- // 1. The chroma is the difference between the maximum and the minimum of the RGB channels,
- // 2. The value is the maximum of the RGB channels, and
- // 3. The saturation comes from dividing the chroma by the maximum of the RGB channels (i.e., the value).
- //
- // From these facts, you can see that we can retrieve the chroma by simply multiplying the saturation and the value,
- // and we can retrieve the minimum of the RGB channels by subtracting the chroma from the value.
- var chroma = saturation * value;
- var min = value - chroma;
-
- // If the chroma is zero, then we have a greyscale color. In that case, the maximum and the minimum RGB channels
- // have the same value (and, indeed, all of the RGB channels are the same), so we can just immediately return
- // the minimum value as the value of all the channels.
- if (chroma == 0)
- {
- return Color.FromArgb(
- (byte)Math.Round(alpha * 255),
- (byte)Math.Round(min * 255),
- (byte)Math.Round(min * 255),
- (byte)Math.Round(min * 255));
- }
-
- // If the chroma is not zero, then we need to continue. The first step is to figure out
- // what section of the color wheel we're located in. In order to do that, we'll divide the hue by 60.
- // The resulting value means we're in one of the following locations:
- //
- // 0 - Between red and yellow.
- // 1 - Between yellow and green.
- // 2 - Between green and cyan.
- // 3 - Between cyan and blue.
- // 4 - Between blue and purple.
- // 5 - Between purple and red.
- //
- // In each of these sextants, one of the RGB channels is completely present, one is partially present, and one is not present.
- // For example, as we transition between red and yellow, red is completely present, green is becoming increasingly present, and blue is not present.
- // Then, as we transition from yellow and green, green is now completely present, red is becoming decreasingly present, and blue is still not present.
- // As we transition from green to cyan, green is still completely present, blue is becoming increasingly present, and red is no longer present. And so on.
- //
- // To convert from hue to RGB value, we first need to figure out which of the three channels is in which configuration
- // in the sextant that we're located in. Next, we figure out what value the completely-present color should have.
- // We know that chroma = (max - min), and we know that this color is the max color, so to find its value we simply add
- // min to chroma to retrieve max. Finally, we consider how far we've transitioned from the pure form of that color
- // to the next color (e.g., how far we are from pure red towards yellow), and give a value to the partially present channel
- // equal to the minimum plus the chroma (i.e., the max minus the min), multiplied by the percentage towards the new color.
- // This gets us a value between the maximum and the minimum representing the partially present channel.
- // Finally, the not-present color must be equal to the minimum value, since it is the one least participating in the overall color.
- int sextant = (int)(hue / 60);
- double intermediateColorPercentage = (hue / 60) - sextant;
- double max = chroma + min;
-
- double r = 0;
- double g = 0;
- double b = 0;
-
- switch (sextant)
- {
- case 0:
- r = max;
- g = min + (chroma * intermediateColorPercentage);
- b = min;
- break;
- case 1:
- r = min + (chroma * (1 - intermediateColorPercentage));
- g = max;
- b = min;
- break;
- case 2:
- r = min;
- g = max;
- b = min + (chroma * intermediateColorPercentage);
- break;
- case 3:
- r = min;
- g = min + (chroma * (1 - intermediateColorPercentage));
- b = max;
- break;
- case 4:
- r = min + (chroma * intermediateColorPercentage);
- g = min;
- b = max;
- break;
- case 5:
- r = max;
- g = min;
- b = min + (chroma * (1 - intermediateColorPercentage));
- break;
- }
-
- return Color.FromArgb(
- (byte)Math.Round(alpha * 255),
- (byte)Math.Round(r * 255),
- (byte)Math.Round(g * 255),
- (byte)Math.Round(b * 255));
- }
-
- ///
- /// Converts the given RGB color to it's HSV color equivalent.
- ///
- /// The color in the RGB color model.
- /// A new equivalent to the given RGBA values.
- public static HsvColor FromRgb(Color color)
- {
- return HsvColor.FromRgb(color.R, color.G, color.B, color.A);
- }
-
- ///
- /// Converts the given RGBA color channel values to it's HSV color equivalent.
- ///
- /// The red channel value in the RGB color model.
- /// The green channel value in the RGB color model.
- /// The blue channel value in the RGB color model.
- /// The alpha channel value.
- /// A new equivalent to the given RGBA values.
- public static HsvColor FromRgb(
- byte red,
- byte green,
- byte blue,
- byte alpha = 0xFF)
- {
- // Note: Conversion code is originally based on the C++ in WinUI (licensed MIT)
- // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/Common/ColorConversion.cpp
- // This was used because it is the best documented and likely most optimized for performance
- // Alpha channel support was added
-
- // Normalize RGBA channel values into the 0..1 range used by this algorithm
- double r = red / 255.0;
- double g = green / 255.0;
- double b = blue / 255.0;
- double a = alpha / 255.0;
-
- double hue;
- double saturation;
- double value;
-
- double max = r >= g ? (r >= b ? r : b) : (g >= b ? g : b);
- double min = r <= g ? (r <= b ? r : b) : (g <= b ? g : b);
-
- // The value, a number between 0 and 1, is the largest of R, G, and B (divided by 255).
- // Conceptually speaking, it represents how much color is present.
- // If at least one of R, G, B is 255, then there exists as much color as there can be.
- // If RGB = (0, 0, 0), then there exists no color at all - a value of zero corresponds
- // to black (i.e., the absence of any color).
- value = max;
-
- // The "chroma" of the color is a value directly proportional to the extent to which
- // the color diverges from greyscale. If, for example, we have RGB = (255, 255, 0),
- // then the chroma is maximized - this is a pure yellow, no gray of any kind.
- // On the other hand, if we have RGB = (128, 128, 128), then the chroma being zero
- // implies that this color is pure greyscale, with no actual hue to be found.
- var chroma = max - min;
-
- // If the chrome is zero, then hue is technically undefined - a greyscale color
- // has no hue. For the sake of convenience, we'll just set hue to zero, since
- // it will be unused in this circumstance. Since the color is purely gray,
- // saturation is also equal to zero - you can think of saturation as basically
- // a measure of hue intensity, such that no hue at all corresponds to a
- // nonexistent intensity.
- if (chroma == 0)
- {
- hue = 0.0;
- saturation = 0.0;
- }
- else
- {
- // In this block, hue is properly defined, so we'll extract both hue
- // and saturation information from the RGB color.
-
- // Hue can be thought of as a cyclical thing, between 0 degrees and 360 degrees.
- // A hue of 0 degrees is red; 120 degrees is green; 240 degrees is blue; and 360 is back to red.
- // Every other hue is somewhere between either red and green, green and blue, and blue and red,
- // so every other hue can be thought of as an angle on this color wheel.
- // These if/else statements determines where on this color wheel our color lies.
- if (r == max)
- {
- // If the red channel is the most pronounced channel, then we exist
- // somewhere between (-60, 60) on the color wheel - i.e., the section around 0 degrees
- // where red dominates. We figure out where in that section we are exactly
- // by considering whether the green or the blue channel is greater - by subtracting green from blue,
- // then if green is greater, we'll nudge ourselves closer to 60, whereas if blue is greater, then
- // we'll nudge ourselves closer to -60. We then divide by chroma (which will actually make the result larger,
- // since chroma is a value between 0 and 1) to normalize the value to ensure that we get the right hue
- // even if we're very close to greyscale.
- hue = 60 * (g - b) / chroma;
- }
- else if (g == max)
- {
- // We do the exact same for the case where the green channel is the most pronounced channel,
- // only this time we want to see if we should tilt towards the blue direction or the red direction.
- // We add 120 to center our value in the green third of the color wheel.
- hue = 120 + (60 * (b - r) / chroma);
- }
- else // blue == max
- {
- // And we also do the exact same for the case where the blue channel is the most pronounced channel,
- // only this time we want to see if we should tilt towards the red direction or the green direction.
- // We add 240 to center our value in the blue third of the color wheel.
- hue = 240 + (60 * (r - g) / chroma);
- }
-
- // Since we want to work within the range [0, 360), we'll add 360 to any value less than zero -
- // this will bump red values from within -60 to -1 to 300 to 359. The hue is the same at both values.
- if (hue < 0.0)
- {
- hue += 360.0;
- }
-
- // The saturation, our final HSV axis, can be thought of as a value between 0 and 1 indicating how intense our color is.
- // To find it, we divide the chroma - the distance between the minimum and the maximum RGB channels - by the maximum channel (i.e., the value).
- // This effectively normalizes the chroma - if the maximum is 0.5 and the minimum is 0, the saturation will be (0.5 - 0) / 0.5 = 1,
- // meaning that although this color is not as bright as it can be, the dark color is as intense as it possibly could be.
- // If, on the other hand, the maximum is 0.5 and the minimum is 0.25, then the saturation will be (0.5 - 0.25) / 0.5 = 0.5,
- // meaning that this color is partially washed out.
- // A saturation value of 0 corresponds to a greyscale color, one in which the color is *completely* washed out and there is no actual hue.
- saturation = chroma / value;
- }
-
- return new HsvColor(a, hue, saturation, value, false);
- }
-
- ///
- /// Indicates whether the values of two specified objects are equal.
- ///
- /// The first object to compare.
- /// The second object to compare.
- /// True if left and right are equal; otherwise, false.
- public static bool operator ==(HsvColor left, HsvColor right)
- {
- return left.Equals(right);
- }
-
- ///
- /// Indicates whether the values of two specified objects are not equal.
- ///
- /// The first object to compare.
- /// The second object to compare.
- /// True if left and right are not equal; otherwise, false.
- public static bool operator !=(HsvColor left, HsvColor right)
- {
- return !(left == right);
- }
-
- ///
- /// Explicit conversion from an to a .
- ///
- /// The to convert.
- public static explicit operator Color(HsvColor hsvColor)
- {
- return hsvColor.ToRgb();
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/IImage.cs b/src/Avalonia.Visuals/Media/IImage.cs
deleted file mode 100644
index aff2a9ddf9..0000000000
--- a/src/Avalonia.Visuals/Media/IImage.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Avalonia.Platform;
-using Avalonia.Visuals.Media.Imaging;
-
-namespace Avalonia.Media
-{
- ///
- /// Represents a raster or vector image.
- ///
- public interface IImage
- {
- ///
- /// Gets the size of the image, in device independent pixels.
- ///
- Size Size { get; }
-
- ///
- /// Draws the image to a .
- ///
- /// The drawing context.
- /// The rect in the image to draw.
- /// The rect in the output to draw to.
- /// The bitmap interpolation mode.
- void Draw(
- DrawingContext context,
- Rect sourceRect,
- Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode);
- }
-}
diff --git a/src/Avalonia.Visuals/Media/ITileBrush.cs b/src/Avalonia.Visuals/Media/ITileBrush.cs
deleted file mode 100644
index 12fb221a89..0000000000
--- a/src/Avalonia.Visuals/Media/ITileBrush.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using Avalonia.Visuals.Media.Imaging;
-
-namespace Avalonia.Media
-{
- ///
- /// A brush which displays a repeating image.
- ///
- public interface ITileBrush : IBrush
- {
- ///
- /// Gets the horizontal alignment of a tile in the destination.
- ///
- AlignmentX AlignmentX { get; }
-
- ///
- /// Gets the horizontal alignment of a tile in the destination.
- ///
- AlignmentY AlignmentY { get; }
-
- ///
- /// Gets the rectangle on the destination in which to paint a tile.
- ///
- RelativeRect DestinationRect { get; }
-
- ///
- /// Gets the rectangle of the source image that will be displayed.
- ///
- RelativeRect SourceRect { get; }
-
- ///
- /// Gets a value indicating how the source rectangle will be stretched to fill the
- /// destination rect.
- ///
- Stretch Stretch { get; }
-
- ///
- /// Gets the brush's tile mode.
- ///
- TileMode TileMode { get; }
-
- ///
- /// Gets the bitmap interpolation mode.
- ///
- ///
- /// The bitmap interpolation mode.
- ///
- BitmapInterpolationMode BitmapInterpolationMode { get; }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
deleted file mode 100644
index 647ce13528..0000000000
--- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-using System;
-using System.IO;
-using Avalonia.Platform;
-using Avalonia.Utilities;
-using Avalonia.Visuals.Media.Imaging;
-
-namespace Avalonia.Media.Imaging
-{
- ///
- /// Holds a bitmap image.
- ///
- public class Bitmap : IBitmap
- {
- ///
- /// Loads a Bitmap from a stream and decodes at the desired width. Aspect ratio is maintained.
- /// This is more efficient than loading and then resizing.
- ///
- /// The stream to read the bitmap from. This can be any supported image format.
- /// The desired width of the resulting bitmap.
- /// The to use should any scaling be required.
- /// An instance of the class.
- public static Bitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
- {
- return new Bitmap(GetFactory().LoadBitmapToWidth(stream, width, interpolationMode));
- }
-
- ///
- /// Loads a Bitmap from a stream and decodes at the desired height. Aspect ratio is maintained.
- /// This is more efficient than loading and then resizing.
- ///
- /// The stream to read the bitmap from. This can be any supported image format.
- /// The desired height of the resulting bitmap.
- /// The to use should any scaling be required.
- /// An instance of the class.
- public static Bitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
- {
- return new Bitmap(GetFactory().LoadBitmapToHeight(stream, height, interpolationMode));
- }
-
- ///
- /// Creates a Bitmap scaled to a specified size from the current bitmap.
- ///
- /// The destination size.
- /// The to use should any scaling be required.
- /// An instance of the class.
- public Bitmap CreateScaledBitmap(PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
- {
- return new Bitmap(GetFactory().ResizeBitmap(PlatformImpl.Item, destinationSize, interpolationMode));
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The filename of the bitmap.
- public Bitmap(string fileName)
- {
- PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(fileName));
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The stream to read the bitmap from.
- public Bitmap(Stream stream)
- {
- PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(stream));
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// A platform-specific bitmap implementation.
- public Bitmap(IRef impl)
- {
- PlatformImpl = impl.Clone();
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// A platform-specific bitmap implementation. Bitmap class takes the ownership.
- protected Bitmap(IBitmapImpl impl)
- {
- PlatformImpl = RefCountable.Create(impl);
- }
-
- ///
- public virtual void Dispose()
- {
- PlatformImpl.Dispose();
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The pixel format.
- /// The pointer to the source bytes.
- /// The size of the bitmap in device pixels.
- /// The DPI of the bitmap.
- /// The number of bytes per row.
- [Obsolete("Use overload taking an AlphaFormat.")]
- public Bitmap(PixelFormat format, IntPtr data, PixelSize size, Vector dpi, int stride)
- {
- var ri = GetFactory();
- PlatformImpl = RefCountable.Create(ri
- .LoadBitmap(format, ri.DefaultAlphaFormat, data, size, dpi, stride));
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The pixel format.
- /// The alpha format.
- /// The pointer to the source bytes.
- /// The size of the bitmap in device pixels.
- /// The DPI of the bitmap.
- /// The number of bytes per row.
- public Bitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
- {
- PlatformImpl = RefCountable.Create(GetFactory().LoadBitmap(format, alphaFormat, data, size, dpi, stride));
- }
-
- ///
- public Vector Dpi => PlatformImpl.Item.Dpi;
-
- ///
- public Size Size => PlatformImpl.Item.PixelSize.ToSizeWithDpi(Dpi);
-
- ///
- public PixelSize PixelSize => PlatformImpl.Item.PixelSize;
-
- ///
- /// Gets the platform-specific bitmap implementation.
- ///
- public IRef PlatformImpl { get; }
-
- ///
- /// Saves the bitmap to a file.
- ///
- /// The filename.
- public void Save(string fileName)
- {
- PlatformImpl.Item.Save(fileName);
- }
-
- ///
- /// Saves the bitmap to a stream.
- ///
- /// The stream.
- public void Save(Stream stream)
- {
- PlatformImpl.Item.Save(stream);
- }
-
- ///
- void IImage.Draw(
- DrawingContext context,
- Rect sourceRect,
- Rect destRect,
- BitmapInterpolationMode bitmapInterpolationMode)
- {
- context.PlatformImpl.DrawBitmap(
- PlatformImpl,
- 1,
- sourceRect,
- destRect,
- bitmapInterpolationMode);
- }
-
- private static IPlatformRenderInterface GetFactory()
- {
- return AvaloniaLocator.Current.GetRequiredService();
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/Imaging/BitmapBlendingMode.cs b/src/Avalonia.Visuals/Media/Imaging/BitmapBlendingMode.cs
deleted file mode 100644
index 473b43dab3..0000000000
--- a/src/Avalonia.Visuals/Media/Imaging/BitmapBlendingMode.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-namespace Avalonia.Visuals.Media.Imaging
-{
- ///
- /// Controls the way the bitmaps are drawn together.
- ///
- public enum BitmapBlendingMode
- {
- ///
- /// Source is placed over the destination.
- ///
- SourceOver,
- ///
- /// Only the source will be present.
- ///
- Source,
- ///
- /// Only the destination will be present.
- ///
- Destination,
- ///
- /// Destination is placed over the source.
- ///
- DestinationOver,
- ///
- /// The source that overlaps the destination, replaces the destination.
- ///
- SourceIn,
- ///
- /// Destination which overlaps the source, replaces the source.
- ///
- DestinationIn,
- ///
- /// Source is placed, where it falls outside of the destination.
- ///
- SourceOut,
- ///
- /// Destination is placed, where it falls outside of the source.
- ///
- DestinationOut,
- ///
- /// Source which overlaps the destination, replaces the destination.
- ///
- SourceAtop,
- ///
- /// Destination which overlaps the source replaces the source.
- ///
- DestinationAtop,
- ///
- /// The non-overlapping regions of source and destination are combined.
- ///
- Xor,
- ///
- /// Display the sum of the source image and destination image.
- ///
- Plus,
- }
-}
diff --git a/src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs b/src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs
deleted file mode 100644
index 5fa48a5ec8..0000000000
--- a/src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace Avalonia.Visuals.Media.Imaging
-{
- ///
- /// Controls the performance and quality of bitmap scaling.
- ///
- public enum BitmapInterpolationMode
- {
- ///
- /// Uses the default behavior of the underling render backend.
- ///
- Default,
-
- ///
- /// The best performance but worst image quality.
- ///
- LowQuality,
-
- ///
- /// Good performance and decent image quality.
- ///
- MediumQuality,
-
- ///
- /// Highest quality but worst performance.
- ///
- HighQuality
- }
-}
diff --git a/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs
deleted file mode 100644
index be1f6db561..0000000000
--- a/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using System;
-using Avalonia.Visuals.Media.Imaging;
-
-namespace Avalonia.Media.Imaging
-{
- ///
- /// Crops a Bitmap.
- ///
- public class CroppedBitmap : AvaloniaObject, IImage, IAffectsRender, IDisposable
- {
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty SourceProperty =
- AvaloniaProperty.Register(nameof(Source));
-
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty SourceRectProperty =
- AvaloniaProperty.Register(nameof(SourceRect));
-
- public event EventHandler? Invalidated;
-
- static CroppedBitmap()
- {
- SourceRectProperty.Changed.AddClassHandler((x, e) => x.SourceRectChanged(e));
- SourceProperty.Changed.AddClassHandler((x, e) => x.SourceChanged(e));
- }
-
- ///
- /// Gets or sets the source for the bitmap.
- ///
- public IImage? Source
- {
- get => GetValue(SourceProperty);
- set => SetValue(SourceProperty, value);
- }
-
- ///
- /// Gets or sets the rectangular area that the bitmap is cropped to.
- ///
- public PixelRect SourceRect
- {
- get => GetValue(SourceRectProperty);
- set => SetValue(SourceRectProperty, value);
- }
-
- public CroppedBitmap()
- {
- Source = null;
- SourceRect = default;
- }
-
- public CroppedBitmap(IImage source, PixelRect sourceRect)
- {
- Source = source;
- SourceRect = sourceRect;
- }
-
- private void SourceChanged(AvaloniaPropertyChangedEventArgs e)
- {
- if (e.NewValue == null)
- return;
- if (!(e.NewValue is IBitmap))
- throw new ArgumentException("Only IBitmap supported as source");
- Invalidated?.Invoke(this, e);
- }
-
- private void SourceRectChanged(AvaloniaPropertyChangedEventArgs e) => Invalidated?.Invoke(this, e);
-
- public virtual void Dispose()
- {
- (Source as IBitmap)?.Dispose();
- }
-
- public Size Size {
- get
- {
- if (Source is not IBitmap bmp)
- return Size.Empty;
- if (SourceRect.IsEmpty)
- return Source.Size;
- return SourceRect.Size.ToSizeWithDpi(bmp.Dpi);
- }
- }
-
- public void Draw(DrawingContext context, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode)
- {
- if (Source is not IBitmap bmp)
- return;
- var topLeft = SourceRect.TopLeft.ToPointWithDpi(bmp.Dpi);
- Source.Draw(context, sourceRect.Translate(new Vector(topLeft.X, topLeft.Y)), destRect, bitmapInterpolationMode);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs
deleted file mode 100644
index 40a24db338..0000000000
--- a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using Avalonia.Platform;
-using Avalonia.Visuals.Media.Imaging;
-
-namespace Avalonia.Media.Imaging
-{
- ///
- /// Holds a writeable bitmap image.
- ///
- public class WriteableBitmap : Bitmap
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The size of the bitmap in device pixels.
- /// The DPI of the bitmap.
- /// The pixel format (optional).
- /// An .
- [Obsolete("Use overload taking an AlphaFormat.")]
- public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat? format = null)
- : base(CreatePlatformImpl(size, dpi, format, null))
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The size of the bitmap in device pixels.
- /// The DPI of the bitmap.
- /// The pixel format (optional).
- /// The alpha format (optional).
- /// An .
- public WriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
- : base(CreatePlatformImpl(size, dpi, format, alphaFormat))
- {
- }
-
- private WriteableBitmap(IWriteableBitmapImpl impl) : base(impl)
- {
-
- }
-
- public ILockedFramebuffer Lock() => ((IWriteableBitmapImpl) PlatformImpl.Item).Lock();
-
- public static WriteableBitmap Decode(Stream stream)
- {
- var ri = GetFactory();
-
- return new WriteableBitmap(ri.LoadWriteableBitmap(stream));
- }
-
- ///
- /// Loads a WriteableBitmap from a stream and decodes at the desired width. Aspect ratio is maintained.
- /// This is more efficient than loading and then resizing.
- ///
- /// The stream to read the bitmap from. This can be any supported image format.
- /// The desired width of the resulting bitmap.
- /// The to use should any scaling be required.
- /// An instance of the class.
- public new static WriteableBitmap DecodeToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
- {
- var ri = GetFactory();
-
- return new WriteableBitmap(ri.LoadWriteableBitmapToWidth(stream, width, interpolationMode));
- }
-
- ///
- /// Loads a Bitmap from a stream and decodes at the desired height. Aspect ratio is maintained.
- /// This is more efficient than loading and then resizing.
- ///
- /// The stream to read the bitmap from. This can be any supported image format.
- /// The desired height of the resulting bitmap.
- /// The to use should any scaling be required.
- /// An instance of the class.
- public new static WriteableBitmap DecodeToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
- {
- var ri = GetFactory();
-
- return new WriteableBitmap(ri.LoadWriteableBitmapToHeight(stream, height, interpolationMode));
- }
-
- private static IBitmapImpl CreatePlatformImpl(PixelSize size, in Vector dpi, PixelFormat? format, AlphaFormat? alphaFormat)
- {
- var ri = GetFactory();
-
- PixelFormat finalFormat = format ?? ri.DefaultPixelFormat;
- AlphaFormat finalAlphaFormat = alphaFormat ?? ri.DefaultAlphaFormat;
-
- return ri.CreateWriteableBitmap(size, dpi, finalFormat, finalAlphaFormat);
- }
-
- private static IPlatformRenderInterface GetFactory()
- {
- return AvaloniaLocator.Current.GetRequiredService();
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs
deleted file mode 100644
index 2d4fe45c8e..0000000000
--- a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using Avalonia.Media.Imaging;
-using Avalonia.Visuals.Media.Imaging;
-
-namespace Avalonia.Media.Immutable
-{
- ///
- /// Paints an area with an .
- ///
- internal class ImmutableImageBrush : ImmutableTileBrush, IImageBrush
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The image to draw.
- /// The horizontal alignment of a tile in the destination.
- /// The vertical alignment of a tile in the destination.
- /// The rectangle on the destination in which to paint a tile.
- /// The opacity of the brush.
- /// The transform of the brush.
- /// The rectangle of the source image that will be displayed.
- ///
- /// How the source rectangle will be stretched to fill the destination rect.
- ///
- /// The tile mode.
- /// The bitmap interpolation mode.
- public ImmutableImageBrush(
- IBitmap source,
- AlignmentX alignmentX = AlignmentX.Center,
- AlignmentY alignmentY = AlignmentY.Center,
- RelativeRect? destinationRect = null,
- double opacity = 1,
- ImmutableTransform? transform = null,
- RelativeRect? sourceRect = null,
- Stretch stretch = Stretch.Uniform,
- TileMode tileMode = TileMode.None,
- BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
- : base(
- alignmentX,
- alignmentY,
- destinationRect ?? RelativeRect.Fill,
- opacity,
- transform,
- sourceRect ?? RelativeRect.Fill,
- stretch,
- tileMode,
- bitmapInterpolationMode)
- {
- Source = source;
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The brush from which this brush's properties should be copied.
- public ImmutableImageBrush(IImageBrush source)
- : base(source)
- {
- Source = source.Source;
- }
-
- ///
- public IBitmap Source { get; }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs
deleted file mode 100644
index 1019751733..0000000000
--- a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using Avalonia.Visuals.Media.Imaging;
-
-namespace Avalonia.Media.Immutable
-{
- ///
- /// A brush which displays a repeating image.
- ///
- public abstract class ImmutableTileBrush : ITileBrush
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The horizontal alignment of a tile in the destination.
- /// The vertical alignment of a tile in the destination.
- /// The rectangle on the destination in which to paint a tile.
- /// The opacity of the brush.
- /// The transform of the brush.
- /// The rectangle of the source image that will be displayed.
- ///
- /// How the source rectangle will be stretched to fill the destination rect.
- ///
- /// The tile mode.
- /// The bitmap interpolation mode.
- protected ImmutableTileBrush(
- AlignmentX alignmentX,
- AlignmentY alignmentY,
- RelativeRect destinationRect,
- double opacity,
- ImmutableTransform? transform,
- RelativeRect sourceRect,
- Stretch stretch,
- TileMode tileMode,
- BitmapInterpolationMode bitmapInterpolationMode)
- {
- AlignmentX = alignmentX;
- AlignmentY = alignmentY;
- DestinationRect = destinationRect;
- Opacity = opacity;
- Transform = transform;
- SourceRect = sourceRect;
- Stretch = stretch;
- TileMode = tileMode;
- BitmapInterpolationMode = bitmapInterpolationMode;
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The brush from which this brush's properties should be copied.
- protected ImmutableTileBrush(ITileBrush source)
- : this(
- source.AlignmentX,
- source.AlignmentY,
- source.DestinationRect,
- source.Opacity,
- source.Transform?.ToImmutable(),
- source.SourceRect,
- source.Stretch,
- source.TileMode,
- source.BitmapInterpolationMode)
- {
- }
-
- ///
- public AlignmentX AlignmentX { get; }
-
- ///
- public AlignmentY AlignmentY { get; }
-
- ///
- public RelativeRect DestinationRect { get; }
-
- ///
- public double Opacity { get; }
-
- ///
- /// Gets the transform of the brush.
- ///
- public ITransform? Transform { get; }
-
- ///
- public RelativeRect SourceRect { get; }
-
- ///
- public Stretch Stretch { get; }
-
- ///
- public TileMode TileMode { get; }
-
- ///
- public BitmapInterpolationMode BitmapInterpolationMode { get; }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs
deleted file mode 100644
index 0fd2905660..0000000000
--- a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using Avalonia.Visuals.Media.Imaging;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Media.Immutable
-{
- ///
- /// Paints an area with an .
- ///
- internal class ImmutableVisualBrush : ImmutableTileBrush, IVisualBrush
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The visual to draw.
- /// The horizontal alignment of a tile in the destination.
- /// The vertical alignment of a tile in the destination.
- /// The rectangle on the destination in which to paint a tile.
- /// The opacity of the brush.
- /// The transform of the brush.
- /// The rectangle of the source image that will be displayed.
- ///
- /// How the source rectangle will be stretched to fill the destination rect.
- ///
- /// The tile mode.
- /// Controls the quality of interpolation.
- public ImmutableVisualBrush(
- IVisual visual,
- AlignmentX alignmentX = AlignmentX.Center,
- AlignmentY alignmentY = AlignmentY.Center,
- RelativeRect? destinationRect = null,
- double opacity = 1,
- ImmutableTransform? transform = null,
- RelativeRect? sourceRect = null,
- Stretch stretch = Stretch.Uniform,
- TileMode tileMode = TileMode.None,
- BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
- : base(
- alignmentX,
- alignmentY,
- destinationRect ?? RelativeRect.Fill,
- opacity,
- transform,
- sourceRect ?? RelativeRect.Fill,
- stretch,
- tileMode,
- bitmapInterpolationMode)
- {
- Visual = visual;
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The brush from which this brush's properties should be copied.
- public ImmutableVisualBrush(IVisualBrush source)
- : base(source)
- {
- Visual = source.Visual;
- }
-
- ///
- public IVisual Visual { get; }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/RenderOptions.cs b/src/Avalonia.Visuals/Media/RenderOptions.cs
deleted file mode 100644
index 7abbc8a656..0000000000
--- a/src/Avalonia.Visuals/Media/RenderOptions.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using Avalonia.Visuals.Media.Imaging;
-
-namespace Avalonia.Media
-{
- public class RenderOptions
- {
- ///
- /// Defines the property.
- ///
- public static readonly StyledProperty BitmapInterpolationModeProperty =
- AvaloniaProperty.RegisterAttached(
- "BitmapInterpolationMode",
- BitmapInterpolationMode.MediumQuality,
- inherits: true);
-
- ///
- /// Gets the value of the BitmapInterpolationMode attached property for a control.
- ///
- /// The control.
- /// The control's left coordinate.
- public static BitmapInterpolationMode GetBitmapInterpolationMode(AvaloniaObject element)
- {
- return element.GetValue(BitmapInterpolationModeProperty);
- }
-
- ///
- /// Sets the value of the BitmapInterpolationMode attached property for a control.
- ///
- /// The control.
- /// The left value.
- public static void SetBitmapInterpolationMode(AvaloniaObject element, BitmapInterpolationMode value)
- {
- element.SetValue(BitmapInterpolationModeProperty, value);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/StreamGeometryContext.cs b/src/Avalonia.Visuals/Media/StreamGeometryContext.cs
deleted file mode 100644
index 88aba8365e..0000000000
--- a/src/Avalonia.Visuals/Media/StreamGeometryContext.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-using Avalonia.Platform;
-
-namespace Avalonia.Media
-{
- ///
- /// Describes a geometry using drawing commands.
- ///
- ///
- /// This class is used to define the geometry of a . An instance
- /// of is obtained by calling
- /// .
- ///
- /// TODO: This class is just a wrapper around IStreamGeometryContextImpl: is it needed?
- public class StreamGeometryContext : IGeometryContext
- {
- private readonly IStreamGeometryContextImpl _impl;
-
- private Point _currentPoint;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The platform-specific implementation.
- public StreamGeometryContext(IStreamGeometryContextImpl impl)
- {
- _impl = impl;
- }
-
- ///
- /// Sets path's winding rule (default is EvenOdd). You should call this method before any calls to BeginFigure. If you wonder why, ask Direct2D guys about their design decisions.
- ///
- ///
-
- public void SetFillRule(FillRule fillRule)
- {
- _impl.SetFillRule(fillRule);
- }
-
- ///
- /// Draws an arc to the specified point.
- ///
- /// The destination point.
- /// The radii of an oval whose perimeter is used to draw the angle.
- /// The rotation angle of the oval that specifies the curve.
- /// true to draw the arc greater than 180 degrees; otherwise, false.
- ///
- /// A value that indicates whether the arc is drawn in the Clockwise or Counterclockwise direction.
- ///
- public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
- {
- _impl.ArcTo(point, size, rotationAngle, isLargeArc, sweepDirection);
- _currentPoint = point;
- }
-
-
- ///
- /// Draws an arc to the specified point using polylines, quadratic or cubic Bezier curves
- /// Significantly more precise when drawing elliptic arcs with extreme width:height ratios.
- ///
- /// The destination point.
- /// The radii of an oval whose perimeter is used to draw the angle.
- /// The rotation angle of the oval that specifies the curve.
- /// true to draw the arc greater than 180 degrees; otherwise, false.
- ///
- /// A value that indicates whether the arc is drawn in the Clockwise or Counterclockwise direction.
- ///
- public void PreciseArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
- {
- PreciseEllipticArcHelper.ArcTo(this, _currentPoint, point, size, rotationAngle, isLargeArc, sweepDirection);
- }
-
- ///
- /// Begins a new figure.
- ///
- /// The starting point for the figure.
- /// Whether the figure is filled.
- public void BeginFigure(Point startPoint, bool isFilled)
- {
- _impl.BeginFigure(startPoint, isFilled);
- _currentPoint = startPoint;
- }
-
- ///
- /// Draws a Bezier curve to the specified point.
- ///
- /// The first control point used to specify the shape of the curve.
- /// The second control point used to specify the shape of the curve.
- /// The destination point for the end of the curve.
- public void CubicBezierTo(Point point1, Point point2, Point point3)
- {
- _impl.CubicBezierTo(point1, point2, point3);
- _currentPoint = point3;
- }
-
- ///
- /// Draws a quadratic Bezier curve to the specified point
- ///
- /// The control point used to specify the shape of the curve.
- /// The destination point for the end of the curve.
- public void QuadraticBezierTo(Point control, Point endPoint)
- {
- _impl.QuadraticBezierTo(control, endPoint);
- _currentPoint = endPoint;
- }
-
- ///
- /// Draws a line to the specified point.
- ///
- /// The destination point.
- public void LineTo(Point point)
- {
- _impl.LineTo(point);
- _currentPoint = point;
- }
-
- ///
- /// Ends the figure started by .
- ///
- /// Whether the figure is closed.
- public void EndFigure(bool isClosed)
- {
- _impl.EndFigure(isClosed);
- }
-
- ///
- /// Finishes the drawing session.
- ///
- public void Dispose()
- {
- _impl.Dispose();
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextDecorationCollection.cs b/src/Avalonia.Visuals/Media/TextDecorationCollection.cs
deleted file mode 100644
index 2dced2252e..0000000000
--- a/src/Avalonia.Visuals/Media/TextDecorationCollection.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Collections;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media
-{
- ///
- /// A collection that holds objects.
- ///
- public class TextDecorationCollection : AvaloniaList
- {
- ///
- /// Parses a string.
- ///
- /// The string.
- /// The .
- public static TextDecorationCollection Parse(string s)
- {
- var locations = new List();
-
- using (var tokenizer = new StringTokenizer(s, ',', "Invalid text decoration."))
- {
- while (tokenizer.TryReadString(out var name))
- {
- var location = GetTextDecorationLocation(name);
-
- if (locations.Contains(location))
- {
- throw new ArgumentException("Text decoration already specified.", nameof(s));
- }
-
- locations.Add(location);
- }
- }
-
- var textDecorations = new TextDecorationCollection();
-
- foreach (var textDecorationLocation in locations)
- {
- textDecorations.Add(new TextDecoration { Location = textDecorationLocation });
- }
-
- return textDecorations;
- }
-
- ///
- /// Parses a string.
- ///
- /// The string.
- /// The .
- private static TextDecorationLocation GetTextDecorationLocation(string s)
- {
- if (Enum.TryParse(s,true, out var location))
- {
- return location;
- }
-
- throw new ArgumentException("Could not parse text decoration.", nameof(s));
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs b/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs
deleted file mode 100644
index 3757a4506a..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// A text run that supports drawing content.
- ///
- public abstract class DrawableTextRun : TextRun
- {
- ///
- /// Gets the size.
- ///
- public abstract Size Size { get; }
-
- ///
- /// Draws the at the given origin.
- ///
- /// The drawing context.
- /// The origin.
- public abstract void Draw(DrawingContext drawingContext, Point origin);
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs b/src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs
deleted file mode 100644
index 1b0feaa718..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Media.TextFormatting.Unicode;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting
-{
- internal readonly struct FormattedTextSource : ITextSource
- {
- private readonly ReadOnlySlice _text;
- private readonly TextRunProperties _defaultProperties;
- private readonly IReadOnlyList>? _textModifier;
-
- public FormattedTextSource(ReadOnlySlice text, TextRunProperties defaultProperties,
- IReadOnlyList>? textModifier)
- {
- _text = text;
- _defaultProperties = defaultProperties;
- _textModifier = textModifier;
- }
-
- public TextRun? GetTextRun(int textSourceIndex)
- {
- if (textSourceIndex > _text.Length)
- {
- return null;
- }
-
- var runText = _text.Skip(textSourceIndex);
-
- if (runText.IsEmpty)
- {
- return new TextEndOfParagraph();
- }
-
- var textStyleRun = CreateTextStyleRun(runText, _defaultProperties, _textModifier);
-
- return new TextCharacters(runText.Take(textStyleRun.Length), textStyleRun.Value);
- }
-
- ///
- /// Creates a span of text run properties that has modifier applied.
- ///
- /// The text to create the properties for.
- /// The default text properties.
- /// The text properties modifier.
- ///
- /// The created text style run.
- ///
- private static ValueSpan CreateTextStyleRun(ReadOnlySlice text,
- TextRunProperties defaultProperties, IReadOnlyList>? textModifier)
- {
- if (textModifier == null || textModifier.Count == 0)
- {
- return new ValueSpan(text.Start, text.Length, defaultProperties);
- }
-
- var currentProperties = defaultProperties;
-
- var hasOverride = false;
-
- var i = 0;
-
- var length = 0;
-
- for (; i < textModifier.Count; i++)
- {
- var propertiesOverride = textModifier[i];
-
- var textRange = new TextRange(propertiesOverride.Start, propertiesOverride.Length);
-
- if (textRange.Start + textRange.Length <= text.Start)
- {
- continue;
- }
-
- if (textRange.Start > text.End)
- {
- length = text.Length;
- break;
- }
-
- if (textRange.Start > text.Start)
- {
- if (propertiesOverride.Value != currentProperties)
- {
- length = Math.Min(Math.Abs(textRange.Start - text.Start), text.Length);
-
- break;
- }
- }
-
- length += Math.Max(0, textRange.Start + textRange.Length - text.Start);
-
- if (hasOverride)
- {
- continue;
- }
-
- hasOverride = true;
-
- currentProperties = propertiesOverride.Value;
- }
-
- if (length < text.Length && i == textModifier.Count)
- {
- if (currentProperties == defaultProperties)
- {
- length = text.Length;
- }
- }
-
- if (length == 0 && currentProperties != defaultProperties)
- {
- currentProperties = defaultProperties;
- length = text.Length;
- }
-
- if (length != text.Length)
- {
- text = text.Take(length);
- }
-
- return new ValueSpan(text.Start, length, currentProperties);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
deleted file mode 100644
index fb85766003..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
+++ /dev/null
@@ -1,203 +0,0 @@
-using System;
-using System.Diagnostics;
-using Avalonia.Media.TextFormatting.Unicode;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// A text run that holds shaped characters.
- ///
- public sealed class ShapedTextCharacters : DrawableTextRun
- {
- private GlyphRun? _glyphRun;
-
- public ShapedTextCharacters(ShapedBuffer shapedBuffer, TextRunProperties properties)
- {
- ShapedBuffer = shapedBuffer;
- Text = shapedBuffer.Text;
- Properties = properties;
- TextSourceLength = Text.Length;
- FontMetrics = new FontMetrics(properties.Typeface, properties.FontRenderingEmSize);
- }
-
- public bool IsReversed { get; private set; }
-
- public sbyte BidiLevel => ShapedBuffer.BidiLevel;
-
- public ShapedBuffer ShapedBuffer { get; }
-
- ///
- public override ReadOnlySlice Text { get; }
-
- ///
- public override TextRunProperties Properties { get; }
-
- ///
- public override int TextSourceLength { get; }
-
- public FontMetrics FontMetrics { get; }
-
- public override Size Size => GlyphRun.Size;
-
- public GlyphRun GlyphRun
- {
- get
- {
- if(_glyphRun is null)
- {
- _glyphRun = CreateGlyphRun();
- }
-
- return _glyphRun;
- }
- }
-
- ///
- public override void Draw(DrawingContext drawingContext, Point origin)
- {
- using (drawingContext.PushPreTransform(Matrix.CreateTranslation(origin)))
- {
- if (GlyphRun.GlyphIndices.Count == 0)
- {
- return;
- }
-
- if (Properties.Typeface == default)
- {
- return;
- }
-
- if (Properties.ForegroundBrush == null)
- {
- return;
- }
-
- if (Properties.BackgroundBrush != null)
- {
- drawingContext.DrawRectangle(Properties.BackgroundBrush, null, new Rect(Size));
- }
-
- drawingContext.DrawGlyphRun(Properties.ForegroundBrush, GlyphRun);
-
- if (Properties.TextDecorations == null)
- {
- return;
- }
-
- foreach (var textDecoration in Properties.TextDecorations)
- {
- textDecoration.Draw(drawingContext, GlyphRun, FontMetrics, Properties.ForegroundBrush);
- }
- }
- }
-
- internal void Reverse()
- {
- _glyphRun = null;
-
- ShapedBuffer.GlyphInfos.Span.Reverse();
-
- IsReversed = !IsReversed;
- }
-
- ///
- /// Measures the number of characters that fit into available width.
- ///
- /// The available width.
- /// The count of fitting characters.
- ///
- /// true if characters fit into the available width; otherwise, false.
- ///
- internal bool TryMeasureCharacters(double availableWidth, out int length)
- {
- length = 0;
- var currentWidth = 0.0;
-
- for (var i = 0; i < ShapedBuffer.Length; i++)
- {
- var advance = ShapedBuffer.GlyphAdvances[i];
-
- if (currentWidth + advance > availableWidth)
- {
- break;
- }
-
- Codepoint.ReadAt(GlyphRun.Characters, length, out var count);
-
- length += count;
- currentWidth += advance;
- }
-
- return length > 0;
- }
-
- internal bool TryMeasureCharactersBackwards(double availableWidth, out int length, out double width)
- {
- length = 0;
- width = 0;
-
- for (var i = ShapedBuffer.Length - 1; i >= 0; i--)
- {
- var advance = ShapedBuffer.GlyphAdvances[i];
-
- if (width + advance > availableWidth)
- {
- break;
- }
-
- Codepoint.ReadAt(GlyphRun.Characters, length, out var count);
-
- length += count;
- width += advance;
- }
-
- return length > 0;
- }
-
- internal SplitResult Split(int length)
- {
- if (IsReversed)
- {
- Reverse();
- }
-
-#if DEBUG
- if(length == 0)
- {
- throw new ArgumentOutOfRangeException(nameof(length), "length must be greater than zero.");
- }
-#endif
-
- var splitBuffer = ShapedBuffer.Split(length);
-
- var first = new ShapedTextCharacters(splitBuffer.First, Properties);
-
- #if DEBUG
-
- if (first.Text.Length != length)
- {
- throw new InvalidOperationException("Split length mismatch.");
- }
-
- #endif
-
- var second = new ShapedTextCharacters(splitBuffer.Second!, Properties);
-
- return new SplitResult(first, second);
- }
-
- internal GlyphRun CreateGlyphRun()
- {
- return new GlyphRun(
- ShapedBuffer.GlyphTypeface,
- ShapedBuffer.FontRenderingEmSize,
- Text,
- ShapedBuffer.GlyphIndices,
- ShapedBuffer.GlyphAdvances,
- ShapedBuffer.GlyphOffsets,
- ShapedBuffer.GlyphClusters,
- BidiLevel);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs
deleted file mode 100644
index c4b2dfb3a5..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs
+++ /dev/null
@@ -1,207 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Media.TextFormatting.Unicode;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// A text run that holds text characters.
- ///
- public class TextCharacters : TextRun
- {
- public TextCharacters(ReadOnlySlice text, TextRunProperties properties)
- {
- TextSourceLength = text.Length;
- Text = text;
- Properties = properties;
- }
-
- public TextCharacters(ReadOnlySlice text, int offsetToFirstCharacter, int length,
- TextRunProperties properties)
- {
- Text = text.Skip(offsetToFirstCharacter).Take(length);
- TextSourceLength = length;
- Properties = properties;
- }
-
- ///
- public override int TextSourceLength { get; }
-
- ///
- public override ReadOnlySlice Text { get; }
-
- ///
- public override TextRunProperties Properties { get; }
-
- ///
- /// Gets a list of .
- ///
- /// The shapeable text characters.
- internal IList GetShapeableCharacters(ReadOnlySlice runText, sbyte biDiLevel,
- ref TextRunProperties? previousProperties)
- {
- var shapeableCharacters = new List(2);
-
- while (!runText.IsEmpty)
- {
- var shapeableRun = CreateShapeableRun(runText, Properties, biDiLevel, ref previousProperties);
-
- shapeableCharacters.Add(shapeableRun);
-
- runText = runText.Skip(shapeableRun.Text.Length);
-
- previousProperties = shapeableRun.Properties;
- }
-
- return shapeableCharacters;
- }
-
- ///
- /// Creates a shapeable text run with unique properties.
- ///
- /// The text to create text runs from.
- /// The default text run properties.
- /// The bidi level of the run.
- ///
- /// A list of shapeable text runs.
- private static ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice text,
- TextRunProperties defaultProperties, sbyte biDiLevel, ref TextRunProperties? previousProperties)
- {
- var defaultTypeface = defaultProperties.Typeface;
- var currentTypeface = defaultTypeface;
- var previousTypeface = previousProperties?.Typeface;
-
- if (TryGetShapeableLength(text, currentTypeface, out var count, out var script))
- {
- if (script == Script.Common && previousTypeface is not null)
- {
- if(TryGetShapeableLength(text, previousTypeface.Value, out var fallbackCount, out _))
- {
- return new ShapeableTextCharacters(text.Take(fallbackCount),
- defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel);
- }
- }
-
- return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface),
- biDiLevel);
- }
-
- if (previousTypeface is not null)
- {
- if(TryGetShapeableLength(text, previousTypeface.Value, out count, out _))
- {
- return new ShapeableTextCharacters(text.Take(count),
- defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel);
- }
- }
-
- var codepoint = Codepoint.ReplacementCodepoint;
-
- var codepointEnumerator = new CodepointEnumerator(text.Skip(count));
-
- while (codepointEnumerator.MoveNext())
- {
- if (codepointEnumerator.Current.IsWhiteSpace)
- {
- continue;
- }
-
- codepoint = codepointEnumerator.Current;
-
- break;
- }
-
- //ToDo: Fix FontFamily fallback
- var matchFound =
- FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight,
- defaultTypeface.Stretch, defaultTypeface.FontFamily, defaultProperties.CultureInfo,
- out currentTypeface);
-
- if (matchFound && TryGetShapeableLength(text, currentTypeface, out count, out _))
- {
- //Fallback found
- return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface),
- biDiLevel);
- }
-
- // no fallback found
- currentTypeface = defaultTypeface;
-
- var glyphTypeface = currentTypeface.GlyphTypeface;
-
- var enumerator = new GraphemeEnumerator(text);
-
- while (enumerator.MoveNext())
- {
- var grapheme = enumerator.Current;
-
- if (!grapheme.FirstCodepoint.IsWhiteSpace && glyphTypeface.TryGetGlyph(grapheme.FirstCodepoint, out _))
- {
- break;
- }
-
- count += grapheme.Text.Length;
- }
-
- return new ShapeableTextCharacters(text.Take(count), defaultProperties, biDiLevel);
- }
-
- ///
- /// Tries to get a shapeable length that is supported by the specified typeface.
- ///
- /// The text.
- /// The typeface that is used to find matching characters.
- /// The shapeable length.
- ///
- ///
- protected static bool TryGetShapeableLength(ReadOnlySlice text, Typeface typeface, out int length,
- out Script script)
- {
- length = 0;
- script = Script.Unknown;
-
- if (text.Length == 0)
- {
- return false;
- }
-
- var font = typeface.GlyphTypeface;
-
- var enumerator = new GraphemeEnumerator(text);
-
- while (enumerator.MoveNext())
- {
- var currentGrapheme = enumerator.Current;
-
- var currentScript = currentGrapheme.FirstCodepoint.Script;
-
- //Stop at the first missing glyph
- if (!currentGrapheme.FirstCodepoint.IsBreakChar && !font.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
- {
- break;
- }
-
- if (currentScript != script)
- {
- if (script is Script.Unknown || currentScript != Script.Common &&
- script is Script.Common or Script.Inherited)
- {
- script = currentScript;
- }
- else
- {
- if (currentScript != Script.Inherited && currentScript != Script.Common)
- {
- break;
- }
- }
- }
-
- length += currentGrapheme.Text.Length;
- }
-
- return length > 0;
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs
deleted file mode 100644
index a46f9537d0..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System.Collections.Generic;
-
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// Properties of text collapsing.
- ///
- public abstract class TextCollapsingProperties
- {
- ///
- /// Gets the width in which the collapsible range is constrained to.
- ///
- public abstract double Width { get; }
-
- ///
- /// Gets the text run that is used as collapsing symbol.
- ///
- public abstract TextRun Symbol { get; }
-
- ///
- /// Collapses given text line.
- ///
- /// Text line to collapse.
- public abstract IReadOnlyList? Collapse(TextLine textLine);
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs
deleted file mode 100644
index 2031c2ec99..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using System.Collections.Generic;
-using Avalonia.Media.TextFormatting.Unicode;
-
-namespace Avalonia.Media.TextFormatting
-{
- internal class TextEllipsisHelper
- {
- public static List? Collapse(TextLine textLine, TextCollapsingProperties properties, bool isWordEllipsis)
- {
- var shapedTextRuns = textLine.TextRuns as List;
-
- if (shapedTextRuns is null)
- {
- return null;
- }
-
- var runIndex = 0;
- var currentWidth = 0.0;
- var collapsedLength = 0;
- var textRange = textLine.TextRange;
- var shapedSymbol = TextFormatterImpl.CreateSymbol(properties.Symbol, FlowDirection.LeftToRight);
-
- if (properties.Width < shapedSymbol.GlyphRun.Size.Width)
- {
- return new List(0);
- }
-
- var availableWidth = properties.Width - shapedSymbol.Size.Width;
-
- while (runIndex < shapedTextRuns.Count)
- {
- var currentRun = shapedTextRuns[runIndex];
-
- currentWidth += currentRun.Size.Width;
-
- if (currentWidth > availableWidth)
- {
- if (currentRun.TryMeasureCharacters(availableWidth, out var measuredLength))
- {
- if (isWordEllipsis && measuredLength < textRange.End)
- {
- var currentBreakPosition = 0;
-
- var lineBreaker = new LineBreakEnumerator(currentRun.Text);
-
- while (currentBreakPosition < measuredLength && lineBreaker.MoveNext())
- {
- var nextBreakPosition = lineBreaker.Current.PositionMeasure;
-
- if (nextBreakPosition == 0)
- {
- break;
- }
-
- if (nextBreakPosition >= measuredLength)
- {
- break;
- }
-
- currentBreakPosition = nextBreakPosition;
- }
-
- measuredLength = currentBreakPosition;
- }
- }
-
- collapsedLength += measuredLength;
-
- var shapedTextCharacters = new List(shapedTextRuns.Count);
-
- if (collapsedLength > 0)
- {
- var splitResult = TextFormatterImpl.SplitShapedRuns(shapedTextRuns, collapsedLength);
-
- shapedTextCharacters.AddRange(splitResult.First);
-
- TextLineImpl.SortRuns(shapedTextCharacters);
- }
-
- shapedTextCharacters.Add(shapedSymbol);
-
- return shapedTextCharacters;
- }
-
- availableWidth -= currentRun.Size.Width;
-
- collapsedLength += currentRun.GlyphRun.Characters.Length;
-
- runIndex++;
- }
-
- return null;
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
deleted file mode 100644
index 13ed850715..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
+++ /dev/null
@@ -1,652 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Runtime.InteropServices;
-using Avalonia.Media.TextFormatting.Unicode;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting
-{
- internal class TextFormatterImpl : TextFormatter
- {
- ///
- public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth,
- TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null)
- {
- var textWrapping = paragraphProperties.TextWrapping;
- FlowDirection flowDirection;
- TextLineBreak? nextLineBreak = null;
- List shapedRuns;
-
- var textRuns = FetchTextRuns(textSource, firstTextSourceIndex,
- out var textEndOfLine, out var textRange);
-
- if (previousLineBreak?.RemainingCharacters != null)
- {
- flowDirection = previousLineBreak.FlowDirection;
- shapedRuns = previousLineBreak.RemainingCharacters.ToList();
- nextLineBreak = previousLineBreak;
- }
- else
- {
- shapedRuns = ShapeTextRuns(textRuns, paragraphProperties.FlowDirection,out flowDirection);
-
- if(nextLineBreak == null && textEndOfLine != null)
- {
- nextLineBreak = new TextLineBreak(textEndOfLine, flowDirection);
- }
- }
-
- TextLineImpl textLine;
-
- switch (textWrapping)
- {
- case TextWrapping.NoWrap:
- {
- TextLineImpl.SortRuns(shapedRuns);
-
- textLine = new TextLineImpl(shapedRuns, textRange, paragraphWidth, paragraphProperties,
- flowDirection, nextLineBreak);
-
- textLine.FinalizeLine();
-
- break;
- }
- case TextWrapping.WrapWithOverflow:
- case TextWrapping.Wrap:
- {
- textLine = PerformTextWrapping(shapedRuns, textRange, paragraphWidth, paragraphProperties,
- flowDirection, nextLineBreak);
- break;
- }
- default:
- throw new ArgumentOutOfRangeException(nameof(textWrapping));
- }
-
- return textLine;
- }
-
- ///
- /// Split a sequence of runs into two segments at specified length.
- ///
- /// The text run's.
- /// The length to split at.
- /// The split text runs.
- internal static SplitResult> SplitShapedRuns(List textRuns, int length)
- {
- var currentLength = 0;
-
- for (var i = 0; i < textRuns.Count; i++)
- {
- var currentRun = textRuns[i];
-
- if (currentLength + currentRun.Text.Length < length)
- {
- currentLength += currentRun.Text.Length;
- continue;
- }
-
- var firstCount = currentRun.Text.Length >= 1 ? i + 1 : i;
-
- var first = new List(firstCount);
-
- if (firstCount > 1)
- {
- for (var j = 0; j < i; j++)
- {
- first.Add(textRuns[j]);
- }
- }
-
- var secondCount = textRuns.Count - firstCount;
-
- if (currentLength + currentRun.Text.Length == length)
- {
- var second = secondCount > 0 ? new List(secondCount) : null;
-
- if (second != null)
- {
- var offset = currentRun.Text.Length >= 1 ? 1 : 0;
-
- for (var j = 0; j < secondCount; j++)
- {
- second.Add(textRuns[i + j + offset]);
- }
- }
-
- first.Add(currentRun);
-
- return new SplitResult>(first, second);
- }
- else
- {
- secondCount++;
-
- var second = new List(secondCount);
-
- var split = currentRun.Split(length - currentLength);
-
- first.Add(split.First);
-
- second.Add(split.Second!);
-
- for (var j = 1; j < secondCount; j++)
- {
- second.Add(textRuns[i + j]);
- }
-
- return new SplitResult>(first, second);
- }
- }
-
- return new SplitResult>(textRuns, null);
- }
-
- ///
- /// Shape specified text runs with specified paragraph embedding.
- ///
- /// The text runs to shape.
- /// The paragraph embedding level.
- /// The resolved flow direction.
- ///
- /// A list of shaped text characters.
- ///
- private static List ShapeTextRuns(List textRuns,
- FlowDirection flowDirection, out FlowDirection resolvedFlowDirection)
- {
- var shapedTextCharacters = new List();
-
- var biDiData = new BidiData((sbyte)flowDirection);
-
- foreach (var textRun in textRuns)
- {
- biDiData.Append(textRun.Text);
- }
-
- var biDi = BidiAlgorithm.Instance.Value!;
-
- biDi.Process(biDiData);
-
- var resolvedEmbeddingLevel = biDi.ResolveEmbeddingLevel(biDiData.Classes);
-
- resolvedFlowDirection =
- (resolvedEmbeddingLevel & 1) == 0 ? FlowDirection.LeftToRight : FlowDirection.RightToLeft;
-
- var shapeableRuns = new List(textRuns.Count);
-
- foreach (var coalescedRuns in CoalesceLevels(textRuns, biDi.ResolvedLevels))
- {
- shapeableRuns.AddRange(coalescedRuns);
- }
-
- for (var index = 0; index < shapeableRuns.Count; index++)
- {
- var currentRun = shapeableRuns[index];
- var groupedRuns = new List(2) { currentRun };
- var text = currentRun.Text;
- var start = currentRun.Text.Start;
- var length = currentRun.Text.Length;
- var bufferOffset = currentRun.Text.BufferOffset;
-
- while (index + 1 < shapeableRuns.Count)
- {
- var nextRun = shapeableRuns[index + 1];
-
- if (currentRun.CanShapeTogether(nextRun))
- {
- groupedRuns.Add(nextRun);
-
- length += nextRun.Text.Length;
-
- if (start > nextRun.Text.Start)
- {
- start = nextRun.Text.Start;
- }
-
- if (bufferOffset > nextRun.Text.BufferOffset)
- {
- bufferOffset = nextRun.Text.BufferOffset;
- }
-
- text = new ReadOnlySlice(text.Buffer, start, length, bufferOffset);
-
- index++;
-
- currentRun = nextRun;
-
- continue;
- }
-
- break;
- }
-
- shapedTextCharacters.AddRange(ShapeTogether(groupedRuns, text));
- }
-
- return shapedTextCharacters;
- }
-
- private static IReadOnlyList ShapeTogether(
- IReadOnlyList textRuns, ReadOnlySlice text)
- {
- var shapedRuns = new List(textRuns.Count);
- var firstRun = textRuns[0];
-
- var shapedBuffer = TextShaper.Current.ShapeText(text, firstRun.Properties.Typeface.GlyphTypeface,
- firstRun.Properties.FontRenderingEmSize, firstRun.Properties.CultureInfo, firstRun.BidiLevel);
-
- for (var i = 0; i < textRuns.Count; i++)
- {
- var currentRun = textRuns[i];
-
- var splitResult = shapedBuffer.Split(currentRun.Text.Length);
-
- shapedRuns.Add(new ShapedTextCharacters(splitResult.First, currentRun.Properties));
-
- shapedBuffer = splitResult.Second!;
- }
-
- return shapedRuns;
- }
-
- ///
- /// Coalesces ranges of the same bidi level to form
- ///
- /// The text characters to form from.
- /// The bidi levels.
- ///
- private static IEnumerable> CoalesceLevels(
- IReadOnlyList textCharacters,
- ReadOnlySlice levels)
- {
- if (levels.Length == 0)
- {
- yield break;
- }
-
- var levelIndex = 0;
- var runLevel = levels[0];
-
- TextRunProperties? previousProperties = null;
- TextCharacters? currentRun = null;
- var runText = ReadOnlySlice.Empty;
-
- for (var i = 0; i < textCharacters.Count; i++)
- {
- var j = 0;
- currentRun = textCharacters[i];
- runText = currentRun.Text;
-
- for (; j < runText.Length;)
- {
- Codepoint.ReadAt(runText, j, out var count);
-
- if (levelIndex + 1 == levels.Length)
- {
- break;
- }
-
- levelIndex++;
- j += count;
-
- if (j == runText.Length)
- {
- yield return currentRun.GetShapeableCharacters(runText.Take(j), runLevel, ref previousProperties);
-
- runLevel = levels[levelIndex];
-
- continue;
- }
-
- if (levels[levelIndex] == runLevel)
- {
- continue;
- }
-
- // End of this run
- yield return currentRun.GetShapeableCharacters(runText.Take(j), runLevel, ref previousProperties);
-
- runText = runText.Skip(j);
-
- j = 0;
-
- // Move to next run
- runLevel = levels[levelIndex];
- }
- }
-
- if (currentRun is null || runText.IsEmpty)
- {
- yield break;
- }
-
- yield return currentRun.GetShapeableCharacters(runText, runLevel, ref previousProperties);
- }
-
- ///
- /// Fetches text runs.
- ///
- /// The text source.
- /// The first text source index.
- ///
- ///
- ///
- /// The formatted text runs.
- ///
- private static List FetchTextRuns(ITextSource textSource, int firstTextSourceIndex,
- out TextEndOfLine? endOfLine, out TextRange textRange)
- {
- var length = 0;
-
- endOfLine = null;
-
- var textRuns = new List();
-
- var textRunEnumerator = new TextRunEnumerator(textSource, firstTextSourceIndex);
-
- while (textRunEnumerator.MoveNext())
- {
- var textRun = textRunEnumerator.Current;
-
- if(textRun == null)
- {
- break;
- }
-
- switch (textRun)
- {
- case TextCharacters textCharacters:
- {
- if (TryGetLineBreak(textCharacters, out var runLineBreak))
- {
- var splitResult = new TextCharacters(textCharacters.Text.Take(runLineBreak.PositionWrap),
- textCharacters.Properties);
-
- textRuns.Add(splitResult);
-
- length += runLineBreak.PositionWrap;
-
- textRange = new TextRange(firstTextSourceIndex, length);
-
- return textRuns;
- }
-
- textRuns.Add(textCharacters);
-
- break;
- }
- case TextEndOfLine textEndOfLine:
- endOfLine = textEndOfLine;
- break;
- }
-
- length += textRun.Text.Length;
- }
-
- textRange = new TextRange(firstTextSourceIndex, length);
-
- return textRuns;
- }
-
- private static bool TryGetLineBreak(TextRun textRun, out LineBreak lineBreak)
- {
- lineBreak = default;
-
- if (textRun.Text.IsEmpty)
- {
- return false;
- }
-
- var lineBreakEnumerator = new LineBreakEnumerator(textRun.Text);
-
- while (lineBreakEnumerator.MoveNext())
- {
- if (!lineBreakEnumerator.Current.Required)
- {
- continue;
- }
-
- lineBreak = lineBreakEnumerator.Current;
-
- return lineBreak.PositionWrap >= textRun.Text.Length || true;
- }
-
- return false;
- }
-
- private static int MeasureLength(IReadOnlyList textRuns, TextRange textRange,
- double paragraphWidth)
- {
- var currentWidth = 0.0;
- var lastCluster = textRange.Start;
-
- foreach (var currentRun in textRuns)
- {
- for (var i = 0; i < currentRun.ShapedBuffer.Length; i++)
- {
- var glyphInfo = currentRun.ShapedBuffer[i];
-
- if (currentWidth + glyphInfo.GlyphAdvance > paragraphWidth)
- {
- var measuredLength = lastCluster - textRange.Start;
-
- return measuredLength == 0 ? 1 : measuredLength;
- }
-
- lastCluster = glyphInfo.GlyphCluster;
- currentWidth += glyphInfo.GlyphAdvance;
- }
- }
-
- return textRange.Length;
- }
-
- ///
- /// Performs text wrapping returns a list of text lines.
- ///
- ///
- /// The text range that is covered by the text runs.
- /// The paragraph width.
- /// The text paragraph properties.
- ///
- /// The current line break if the line was explicitly broken.
- /// The wrapped text line.
- private static TextLineImpl PerformTextWrapping(List textRuns, TextRange textRange,
- double paragraphWidth, TextParagraphProperties paragraphProperties, FlowDirection flowDirection,
- TextLineBreak? currentLineBreak)
- {
- var measuredLength = MeasureLength(textRuns, textRange, paragraphWidth);
-
- var currentLength = 0;
-
- var lastWrapPosition = 0;
-
- var currentPosition = 0;
-
- for (var index = 0; index < textRuns.Count; index++)
- {
- var currentRun = textRuns[index];
-
- var lineBreaker = new LineBreakEnumerator(currentRun.Text);
-
- var breakFound = false;
-
- while (lineBreaker.MoveNext())
- {
- if (lineBreaker.Current.Required &&
- currentLength + lineBreaker.Current.PositionMeasure <= measuredLength)
- {
- //Explicit break found
- breakFound = true;
-
- currentPosition = currentLength + lineBreaker.Current.PositionWrap;
-
- break;
- }
-
- if (currentLength + lineBreaker.Current.PositionMeasure > measuredLength)
- {
- if (paragraphProperties.TextWrapping == TextWrapping.WrapWithOverflow)
- {
- if (lastWrapPosition > 0)
- {
- currentPosition = lastWrapPosition;
-
- breakFound = true;
-
- break;
- }
-
- //Find next possible wrap position (overflow)
- if (index < textRuns.Count - 1)
- {
- if (lineBreaker.Current.PositionWrap != currentRun.Text.Length)
- {
- //We already found the next possible wrap position.
- breakFound = true;
-
- currentPosition = currentLength + lineBreaker.Current.PositionWrap;
-
- break;
- }
-
- while (lineBreaker.MoveNext() && index < textRuns.Count)
- {
- currentPosition += lineBreaker.Current.PositionWrap;
-
- if (lineBreaker.Current.PositionWrap != currentRun.Text.Length)
- {
- break;
- }
-
- index++;
-
- if (index >= textRuns.Count)
- {
- break;
- }
-
- currentRun = textRuns[index];
-
- lineBreaker = new LineBreakEnumerator(currentRun.Text);
- }
- }
- else
- {
- currentPosition = currentLength + lineBreaker.Current.PositionWrap;
- }
-
- breakFound = true;
-
- break;
- }
-
- //We overflowed so we use the last available wrap position.
- currentPosition = lastWrapPosition == 0 ? measuredLength : lastWrapPosition;
-
- breakFound = true;
-
- break;
- }
-
- if (lineBreaker.Current.PositionMeasure != lineBreaker.Current.PositionWrap)
- {
- lastWrapPosition = currentLength + lineBreaker.Current.PositionWrap;
- }
- }
-
- if (!breakFound)
- {
- currentLength += currentRun.Text.Length;
-
- continue;
- }
-
- measuredLength = currentPosition;
-
- break;
- }
-
- var splitResult = SplitShapedRuns(textRuns, measuredLength);
-
- textRange = new TextRange(textRange.Start, measuredLength);
-
- var remainingCharacters = splitResult.Second;
-
- var lineBreak = remainingCharacters?.Count > 0 ?
- new TextLineBreak(currentLineBreak?.TextEndOfLine, flowDirection, remainingCharacters) :
- null;
-
- if (lineBreak is null && currentLineBreak?.TextEndOfLine != null)
- {
- lineBreak = new TextLineBreak(currentLineBreak.TextEndOfLine, flowDirection);
- }
-
- TextLineImpl.SortRuns(splitResult.First);
-
- var textLine = new TextLineImpl(splitResult.First, textRange, paragraphWidth, paragraphProperties, flowDirection,
- lineBreak);
-
- return textLine.FinalizeLine();
- }
-
- private struct TextRunEnumerator
- {
- private readonly ITextSource _textSource;
- private int _pos;
-
- public TextRunEnumerator(ITextSource textSource, int firstTextSourceIndex)
- {
- _textSource = textSource;
- _pos = firstTextSourceIndex;
- Current = null;
- }
-
- // ReSharper disable once MemberHidesStaticFromOuterClass
- public TextRun? Current { get; private set; }
-
- public bool MoveNext()
- {
- Current = _textSource.GetTextRun(_pos);
-
- if (Current is null)
- {
- return false;
- }
-
- if (Current.TextSourceLength == 0)
- {
- return false;
- }
-
- _pos += Current.TextSourceLength;
-
- return true;
- }
- }
-
- ///
- /// Creates a shaped symbol.
- ///
- /// The symbol run to shape.
- /// The flow direction.
- ///
- /// The shaped symbol.
- ///
- internal static ShapedTextCharacters CreateSymbol(TextRun textRun, FlowDirection flowDirection)
- {
- var textShaper = TextShaper.Current;
-
- var glyphTypeface = textRun.Properties!.Typeface.GlyphTypeface;
-
- var fontRenderingEmSize = textRun.Properties.FontRenderingEmSize;
-
- var cultureInfo = textRun.Properties.CultureInfo;
-
- var shapedBuffer = textShaper.ShapeText(textRun.Text, glyphTypeface, fontRenderingEmSize, cultureInfo, (sbyte)flowDirection);
-
- return new ShapedTextCharacters(shapedBuffer, textRun.Properties);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
deleted file mode 100644
index 0ff127694b..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
+++ /dev/null
@@ -1,647 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// Represents a multi line text layout.
- ///
- public class TextLayout
- {
- private static readonly char[] s_empty = { ' ' };
-
- private readonly ReadOnlySlice _text;
- private readonly TextParagraphProperties _paragraphProperties;
- private readonly IReadOnlyList>? _textStyleOverrides;
- private readonly TextTrimming _textTrimming;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The text.
- /// The typeface.
- /// Size of the font.
- /// The foreground.
- /// The text alignment.
- /// The text wrapping.
- /// The text trimming.
- /// The text decorations.
- /// The text flow direction.
- /// The maximum width.
- /// The maximum height.
- /// The height of each line of text.
- /// The maximum number of text lines.
- /// The text style overrides.
- public TextLayout(
- string? text,
- Typeface typeface,
- double fontSize,
- IBrush? foreground,
- TextAlignment textAlignment = TextAlignment.Left,
- TextWrapping textWrapping = TextWrapping.NoWrap,
- TextTrimming? textTrimming = null,
- TextDecorationCollection? textDecorations = null,
- FlowDirection flowDirection = FlowDirection.LeftToRight,
- double maxWidth = double.PositiveInfinity,
- double maxHeight = double.PositiveInfinity,
- double lineHeight = double.NaN,
- int maxLines = 0,
- IReadOnlyList>? textStyleOverrides = null)
- {
- _text = string.IsNullOrEmpty(text) ?
- new ReadOnlySlice() :
- new ReadOnlySlice(text.AsMemory());
-
- _paragraphProperties =
- CreateTextParagraphProperties(typeface, fontSize, foreground, textAlignment, textWrapping,
- textDecorations, flowDirection, lineHeight);
-
- _textTrimming = textTrimming ?? TextTrimming.None;
-
- _textStyleOverrides = textStyleOverrides;
-
- LineHeight = lineHeight;
-
- MaxWidth = maxWidth;
-
- MaxHeight = maxHeight;
-
- MaxLines = maxLines;
-
- TextLines = CreateTextLines();
- }
-
- ///
- /// Gets or sets the height of each line of text.
- ///
- ///
- /// A value of NaN (equivalent to an attribute value of "Auto") indicates that the line height
- /// is determined automatically from the current font characteristics. The default is NaN.
- ///
- public double LineHeight { get; }
-
- ///
- /// Gets the maximum width.
- ///
- public double MaxWidth { get; }
-
- ///
- /// Gets the maximum height.
- ///
- public double MaxHeight { get; }
-
- ///
- /// Gets the maximum number of text lines.
- ///
- public int MaxLines { get; }
-
- ///
- /// Gets the text lines.
- ///
- ///
- /// The text lines.
- ///
- public IReadOnlyList TextLines { get; private set; }
-
- ///
- /// Gets the bounds of the layout.
- ///
- ///
- /// The bounds.
- ///
- public Rect Bounds { get; private set; }
-
- ///
- /// Draws the text layout.
- ///
- /// The drawing context.
- /// The origin.
- public void Draw(DrawingContext context, Point origin)
- {
- if (!TextLines.Any())
- {
- return;
- }
-
- var (currentX, currentY) = origin;
-
- foreach (var textLine in TextLines)
- {
- textLine.Draw(context, new Point(currentX + textLine.Start, currentY));
-
- currentY += textLine.Height;
- }
- }
-
- ///
- /// Get the pixel location relative to the top-left of the layout box given the text position.
- ///
- /// The text position.
- ///
- public Rect HitTestTextPosition(int textPosition)
- {
- if (TextLines.Count == 0)
- {
- return new Rect();
- }
-
- if (textPosition < 0 || textPosition >= _text.Length)
- {
- var lastLine = TextLines[TextLines.Count - 1];
-
- var lineX = lastLine.Width;
-
- var lineY = Bounds.Bottom - lastLine.Height;
-
- return new Rect(lineX, lineY, 0, lastLine.Height);
- }
-
- var currentY = 0.0;
-
- foreach (var textLine in TextLines)
- {
- if (textLine.TextRange.End < textPosition)
- {
- currentY += textLine.Height;
-
- continue;
- }
-
- var characterHit = new CharacterHit(textPosition);
-
- var startX = textLine.GetDistanceFromCharacterHit(characterHit);
-
- var nextCharacterHit = textLine.GetNextCaretCharacterHit(characterHit);
-
- var endX = textLine.GetDistanceFromCharacterHit(nextCharacterHit);
-
- return new Rect(startX, currentY, endX - startX, textLine.Height);
- }
-
- return new Rect();
- }
-
- public IEnumerable HitTestTextRange(int start, int length)
- {
- if (start + length <= 0)
- {
- return Array.Empty();
- }
-
- var result = new List(TextLines.Count);
-
- var currentY = 0d;
- var currentPosition = 0;
- var currentRect = Rect.Empty;
-
- foreach (var textLine in TextLines)
- {
- //Current line isn't covered.
- if (currentPosition + textLine.TextRange.Length <= start)
- {
- currentY += textLine.Height;
- currentPosition += textLine.TextRange.Length;
-
- continue;
- }
-
- //The whole line is covered.
- if (currentPosition >= start && start + length > currentPosition + textLine.TextRange.Length)
- {
- currentRect = new Rect(textLine.Start, currentY, textLine.WidthIncludingTrailingWhitespace,
- textLine.Height);
-
- result.Add(currentRect);
-
- currentY += textLine.Height;
- currentPosition += textLine.TextRange.Length;
-
- continue;
- }
-
- var startX = textLine.Start;
-
- //A portion of the line is covered.
- for (var index = 0; index < textLine.TextRuns.Count; index++)
- {
- var currentRun = (ShapedTextCharacters)textLine.TextRuns[index];
- ShapedTextCharacters? nextRun = null;
-
- if (index + 1 < textLine.TextRuns.Count)
- {
- nextRun = (ShapedTextCharacters)textLine.TextRuns[index + 1];
- }
-
- if (nextRun != null)
- {
- if (nextRun.Text.Start < currentRun.Text.Start && start + length < currentRun.Text.End)
- {
- goto skip;
- }
-
- if (currentRun.Text.Start >= start + length)
- {
- goto skip;
- }
-
- if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < start)
- {
- goto skip;
- }
-
- if (currentRun.Text.End < start)
- {
- goto skip;
- }
-
- goto noop;
-
- skip:
- {
- startX += currentRun.Size.Width;
-
- currentPosition = currentRun.Text.Start;
- }
-
- continue;
-
- noop:{ }
- }
-
- var endOffset = currentRun.GlyphRun.GetDistanceFromCharacterHit(
- currentRun.ShapedBuffer.IsLeftToRight ?
- new CharacterHit(start + length) :
- new CharacterHit(start));
-
- var endX = startX + endOffset;
-
- var startOffset = currentRun.GlyphRun.GetDistanceFromCharacterHit(
- currentRun.ShapedBuffer.IsLeftToRight ?
- new CharacterHit(start) :
- new CharacterHit(start + length));
-
- startX += startOffset;
-
- var characterHit = currentRun.GlyphRun.IsLeftToRight ?
- currentRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) :
- currentRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
-
- currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
-
- if(nextRun != null)
- {
- if (currentRun.ShapedBuffer.IsLeftToRight == nextRun.ShapedBuffer.IsLeftToRight)
- {
- endOffset = nextRun.GlyphRun.GetDistanceFromCharacterHit(
- nextRun.ShapedBuffer.IsLeftToRight ?
- new CharacterHit(start + length) :
- new CharacterHit(start));
-
- index++;
-
- endX += endOffset;
-
- currentRun = nextRun;
-
- if (currentRun.ShapedBuffer.IsLeftToRight)
- {
- characterHit = nextRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
-
- currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
- }
- }
- }
-
- if (endX < startX)
- {
- (endX, startX) = (startX, endX);
- }
-
- var width = endX - startX;
-
- if (result.Count > 0 && MathUtilities.AreClose(currentRect.Top, currentY) &&
- MathUtilities.AreClose(currentRect.Right, startX))
- {
- result[result.Count - 1] = currentRect.WithWidth(currentRect.Width + width);
- }
- else
- {
- currentRect = new Rect(startX, currentY, width, textLine.Height);
-
- result.Add(currentRect);
- }
-
- if (currentRun.ShapedBuffer.IsLeftToRight)
- {
- if (nextRun != null)
- {
- if (nextRun.Text.Start > currentRun.Text.Start && nextRun.Text.Start >= start + length)
- {
- break;
- }
-
- currentPosition = nextRun.Text.End;
- }
- else
- {
- if (currentPosition >= start + length)
- {
- break;
- }
- }
- }
- else
- {
- if (currentPosition <= start)
- {
- break;
- }
- }
-
- if (!currentRun.ShapedBuffer.IsLeftToRight && currentPosition != currentRun.Text.Start)
- {
- endX += currentRun.GlyphRun.Size.Width - endOffset;
- }
-
- startX = endX;
- }
-
- if (currentPosition == start || currentPosition == start + length)
- {
- break;
- }
-
- if (textLine.TextRange.Start + textLine.TextRange.Length >= start + length)
- {
- break;
- }
-
- currentY += textLine.Height;
- }
-
- return result;
- }
-
- public TextHitTestResult HitTestPoint(in Point point)
- {
- var currentY = 0d;
-
- var lineIndex = 0;
- TextLine? currentLine = null;
- CharacterHit characterHit;
-
- for (; lineIndex < TextLines.Count; lineIndex++)
- {
- currentLine = TextLines[lineIndex];
-
- if (currentY + currentLine.Height > point.Y)
- {
- characterHit = currentLine.GetCharacterHitFromDistance(point.X);
-
- return GetHitTestResult(currentLine, characterHit, point);
- }
-
- currentY += currentLine.Height;
- }
-
- if (currentLine is null)
- {
- return new TextHitTestResult();
- }
-
- characterHit = currentLine.GetCharacterHitFromDistance(point.X);
-
- return GetHitTestResult(currentLine, characterHit, point);
- }
-
-
- public int GetLineIndexFromCharacterIndex(int charIndex, bool trailingEdge)
- {
- if (charIndex < 0)
- {
- return 0;
- }
-
- if (charIndex > _text.Length)
- {
- return TextLines.Count - 1;
- }
-
- for (var index = 0; index < TextLines.Count; index++)
- {
- var textLine = TextLines[index];
-
- if (textLine.TextRange.Start + textLine.TextRange.Length < charIndex)
- {
- continue;
- }
-
- if (charIndex >= textLine.TextRange.Start && charIndex <= textLine.TextRange.End + (trailingEdge ? 1 : 0))
- {
- return index;
- }
- }
-
- return TextLines.Count - 1;
- }
-
- private TextHitTestResult GetHitTestResult(TextLine textLine, CharacterHit characterHit, Point point)
- {
- var (x, y) = point;
-
- var lastTrailingIndex = textLine.TextRange.Start + textLine.TextRange.Length;
-
- var isInside = x >= 0 && x <= textLine.Width && y >= 0 && y <= textLine.Height;
-
- if (x >= textLine.Width && textLine.TextRange.Length > 0 && textLine.NewLineLength > 0)
- {
- lastTrailingIndex -= textLine.NewLineLength;
- }
-
- var textPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
-
- var isTrailing = lastTrailingIndex == textPosition && characterHit.TrailingLength > 0 ||
- y > Bounds.Bottom;
-
- if (textPosition == textLine.TextRange.Start + textLine.TextRange.Length)
- {
- textPosition -= textLine.NewLineLength;
- }
-
- if (textLine.NewLineLength > 0 && textPosition + textLine.NewLineLength ==
- characterHit.FirstCharacterIndex + characterHit.TrailingLength)
- {
- characterHit = new CharacterHit(characterHit.FirstCharacterIndex);
- }
-
- return new TextHitTestResult(characterHit, textPosition, isInside, isTrailing);
- }
-
- ///
- /// Creates the default that are used by the .
- ///
- /// The typeface.
- /// The font size.
- /// The foreground.
- /// The text alignment.
- /// The text wrapping.
- /// The text decorations.
- /// The text flow direction.
- /// The height of each line of text.
- ///
- private static TextParagraphProperties CreateTextParagraphProperties(Typeface typeface, double fontSize,
- IBrush? foreground, TextAlignment textAlignment, TextWrapping textWrapping,
- TextDecorationCollection? textDecorations, FlowDirection flowDirection, double lineHeight)
- {
- var textRunStyle = new GenericTextRunProperties(typeface, fontSize, textDecorations, foreground);
-
- return new GenericTextParagraphProperties(flowDirection, textAlignment, true, false,
- textRunStyle, textWrapping, lineHeight, 0);
- }
-
- ///
- /// Updates the current bounds.
- ///
- /// The text line.
- /// The current left.
- /// The current width.
- /// The current height.
- private static void UpdateBounds(TextLine textLine,ref double left, ref double width, ref double height)
- {
- var lineWidth = textLine.WidthIncludingTrailingWhitespace;
-
- if (width < lineWidth)
- {
- width = lineWidth;
- }
-
- if (left > textLine.Start)
- {
- left = textLine.Start;
- }
-
- height += textLine.Height;
- }
-
- ///
- /// Creates an empty text line.
- ///
- /// The empty text line.
- private TextLine CreateEmptyTextLine(int startingIndex)
- {
- var flowDirection = _paragraphProperties.FlowDirection;
- var properties = _paragraphProperties.DefaultTextRunProperties;
- var glyphTypeface = properties.Typeface.GlyphTypeface;
- var text = new ReadOnlySlice(s_empty, startingIndex, 1);
- var glyph = glyphTypeface.GetGlyph(s_empty[0]);
- var glyphInfos = new[] { new GlyphInfo(glyph, startingIndex) };
-
- var shapedBuffer = new ShapedBuffer(text, glyphInfos, glyphTypeface, properties.FontRenderingEmSize,
- (sbyte)flowDirection);
-
- var textRuns = new List { new ShapedTextCharacters(shapedBuffer, properties) };
-
- var textRange = new TextRange(startingIndex, 1);
-
- return new TextLineImpl(textRuns, textRange, MaxWidth, _paragraphProperties, flowDirection).FinalizeLine();
- }
-
- private IReadOnlyList CreateTextLines()
- {
- if (_text.IsEmpty || MathUtilities.IsZero(MaxWidth) || MathUtilities.IsZero(MaxHeight))
- {
- var textLine = CreateEmptyTextLine(0);
-
- Bounds = new Rect(0,0,0, textLine.Height);
-
- return new List { textLine };
- }
-
- var textLines = new List();
-
- double left = double.PositiveInfinity, width = 0.0, height = 0.0;
-
- var currentPosition = 0;
-
- var textSource = new FormattedTextSource(_text,
- _paragraphProperties.DefaultTextRunProperties, _textStyleOverrides);
-
- TextLine? previousLine = null;
-
- while (currentPosition < _text.Length)
- {
- var textLine = TextFormatter.Current.FormatLine(textSource, currentPosition, MaxWidth,
- _paragraphProperties, previousLine?.TextLineBreak);
-
-#if DEBUG
- if (textLine.TextRange.Length == 0)
- {
- throw new InvalidOperationException($"{nameof(textLine)} should not be empty.");
- }
-#endif
-
- currentPosition += textLine.TextRange.Length;
-
- //Fulfill max height constraint
- if (textLines.Count > 0 && !double.IsPositiveInfinity(MaxHeight) && height + textLine.Height > MaxHeight)
- {
- if (previousLine?.TextLineBreak != null && _textTrimming != TextTrimming.None)
- {
- var collapsedLine =
- previousLine.Collapse(GetCollapsingProperties(MaxWidth));
-
- textLines[textLines.Count - 1] = collapsedLine;
- }
-
- break;
- }
-
- var hasOverflowed = textLine.HasOverflowed;
-
- if (hasOverflowed && _textTrimming != TextTrimming.None)
- {
- textLine = textLine.Collapse(GetCollapsingProperties(MaxWidth));
- }
-
- textLines.Add(textLine);
-
- UpdateBounds(textLine, ref left, ref width, ref height);
-
- previousLine = textLine;
-
- //Fulfill max lines constraint
- if (MaxLines > 0 && textLines.Count >= MaxLines)
- {
- break;
- }
-
- if (currentPosition != _text.Length || textLine.NewLineLength <= 0)
- {
- continue;
- }
-
- var emptyTextLine = CreateEmptyTextLine(currentPosition);
-
- textLines.Add(emptyTextLine);
-
- UpdateBounds(emptyTextLine,ref left, ref width, ref height);
- }
-
- Bounds = new Rect(left, 0, width, height);
-
- return textLines;
- }
-
- ///
- /// Gets the for current text trimming mode.
- ///
- /// The collapsing width.
- /// The .
- private TextCollapsingProperties GetCollapsingProperties(double width)
- {
- return _textTrimming.CreateCollapsingProperties(new TextCollapsingCreateInfo(width, _paragraphProperties.DefaultTextRunProperties));
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
deleted file mode 100644
index 74c4573630..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// Ellipsis based on a fixed length leading prefix and suffix growing from the end at character granularity.
- ///
- public sealed class TextLeadingPrefixCharacterEllipsis : TextCollapsingProperties
- {
- private readonly int _prefixLength;
-
- ///
- /// Construct a text trailing word ellipsis collapsing properties.
- ///
- /// Text used as collapsing symbol.
- /// Length of leading prefix.
- /// width in which collapsing is constrained to
- /// text run properties of ellispis symbol
- public TextLeadingPrefixCharacterEllipsis(
- ReadOnlySlice ellipsis,
- int prefixLength,
- double width,
- TextRunProperties textRunProperties)
- {
- if (_prefixLength < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(prefixLength));
- }
-
- _prefixLength = prefixLength;
- Width = width;
- Symbol = new TextCharacters(ellipsis, textRunProperties);
- }
-
- ///
- public sealed override double Width { get; }
-
- ///
- public sealed override TextRun Symbol { get; }
-
- public override IReadOnlyList? Collapse(TextLine textLine)
- {
- var shapedTextRuns = textLine.TextRuns as List;
-
- if (shapedTextRuns is null)
- {
- return null;
- }
-
- var runIndex = 0;
- var currentWidth = 0.0;
- var shapedSymbol = TextFormatterImpl.CreateSymbol(Symbol, FlowDirection.LeftToRight);
-
- if (Width < shapedSymbol.GlyphRun.Size.Width)
- {
- return new List(0);
- }
-
- // Overview of ellipsis structure
- // Prefix length run | Ellipsis symbol | Post split run growing from the end |
- var availableWidth = Width - shapedSymbol.Size.Width;
-
- while (runIndex < shapedTextRuns.Count)
- {
- var currentRun = shapedTextRuns[runIndex];
-
- currentWidth += currentRun.Size.Width;
-
- if (currentWidth > availableWidth)
- {
- currentRun.TryMeasureCharacters(availableWidth, out var measuredLength);
-
- var shapedTextCharacters = new List(shapedTextRuns.Count);
-
- if (measuredLength > 0)
- {
- List? preSplitRuns = null;
- List? postSplitRuns = null;
-
- if (_prefixLength > 0)
- {
- var splitResult = TextFormatterImpl.SplitShapedRuns(shapedTextRuns, Math.Min(_prefixLength, measuredLength));
-
- shapedTextCharacters.AddRange(splitResult.First);
-
- TextLineImpl.SortRuns(shapedTextCharacters);
-
- preSplitRuns = splitResult.First;
- postSplitRuns = splitResult.Second;
- }
- else
- {
- postSplitRuns = shapedTextRuns;
- }
-
- shapedTextCharacters.Add(shapedSymbol);
-
- if (measuredLength > _prefixLength && postSplitRuns is not null)
- {
- var availableSuffixWidth = availableWidth;
-
- if (preSplitRuns is not null)
- {
- foreach (var run in preSplitRuns)
- {
- availableSuffixWidth -= run.Size.Width;
- }
- }
-
- for (int i = postSplitRuns.Count - 1; i >= 0; i--)
- {
- var run = postSplitRuns[i];
-
- if (run.TryMeasureCharactersBackwards(availableSuffixWidth, out int suffixCount, out double suffixWidth))
- {
- availableSuffixWidth -= suffixWidth;
-
- if (suffixCount > 0)
- {
- var splitSuffix = run.Split(run.TextSourceLength - suffixCount);
-
- shapedTextCharacters.Add(splitSuffix.Second!);
- }
- }
- }
- }
- }
- else
- {
- shapedTextCharacters.Add(shapedSymbol);
- }
-
- return shapedTextCharacters;
- }
-
- availableWidth -= currentRun.Size.Width;
-
- runIndex++;
- }
-
- return null;
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
deleted file mode 100644
index 130d0e9c39..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
+++ /dev/null
@@ -1,237 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// Represents a line of text that is used for text rendering.
- ///
- public abstract class TextLine
- {
- ///
- /// Gets the text runs that are contained within a line.
- ///
- ///
- /// The contained text runs.
- ///
- public abstract IReadOnlyList TextRuns { get; }
-
- ///
- /// Gets the text range that is covered by the line.
- ///
- ///
- /// The text range that is covered by the line.
- ///
- public abstract TextRange TextRange { get; }
-
- ///
- /// Gets the state of the line when broken by line breaking process.
- ///
- ///
- /// A value that represents the line break.
- ///
- public abstract TextLineBreak? TextLineBreak { get; }
-
- ///
- /// Gets the distance from the top to the baseline of the current TextLine object.
- ///
- ///
- /// A that represents the baseline distance.
- ///
- public abstract double Baseline { get; }
-
- ///
- /// Gets the distance from the top-most to bottom-most black pixel in a line.
- ///
- ///
- /// A value that represents the extent distance.
- ///
- public abstract double Extent { get; }
-
- ///
- /// Gets a value that indicates whether the line is collapsed.
- ///
- ///
- /// true, if the line is collapsed; otherwise, false.
- ///
- public abstract bool HasCollapsed { get; }
-
- ///
- /// Gets a value that indicates whether content of the line overflows the specified paragraph width.
- ///
- ///
- /// true, it the line overflows the specified paragraph width; otherwise, false.
- ///
- public abstract bool HasOverflowed { get; }
-
- ///
- /// Gets the height of a line of text.
- ///
- ///
- /// The text line height.
- ///
- public abstract double Height { get; }
-
- ///
- /// Gets the number of newline characters at the end of a line.
- ///
- ///
- /// The number of newline characters.
- ///
- public abstract int NewLineLength { get; }
-
- ///
- /// Gets the distance that black pixels extend beyond the bottom alignment edge of a line.
- ///
- ///
- /// The overhang after distance.
- ///
- public abstract double OverhangAfter { get; }
-
- ///
- /// Gets the distance that black pixels extend prior to the left leading alignment edge of the line.
- ///
- ///
- /// The overhang leading distance.
- ///
- public abstract double OverhangLeading { get; }
-
- ///
- /// Gets the distance that black pixels extend following the right trailing alignment edge of the line.
- ///
- ///
- /// The overhang trailing distance.
- ///
- public abstract double OverhangTrailing { get; }
-
- ///
- /// Gets the distance from the start of a paragraph to the starting point of a line.
- ///
- ///
- /// The distance from the start of a paragraph to the starting point of a line.
- ///
- public abstract double Start { get; }
-
- ///
- /// Gets the number of whitespace code points beyond the last non-blank character in a line.
- ///
- ///
- /// The number of whitespace code points beyond the last non-blank character in a line.
- ///
- public abstract int TrailingWhitespaceLength { get; }
-
- ///
- /// Gets the width of a line of text, excluding trailing whitespace characters.
- ///
- ///
- /// The text line width, excluding trailing whitespace characters.
- ///
- public abstract double Width { get; }
-
- ///
- /// Gets the width of a line of text, including trailing whitespace characters.
- ///
- ///
- /// The text line width, including trailing whitespace characters.
- ///
- public abstract double WidthIncludingTrailingWhitespace { get; }
-
- ///
- /// Draws the at the given origin.
- ///
- /// The drawing context.
- ///
- public abstract void Draw(DrawingContext drawingContext, Point lineOrigin);
-
- ///
- /// Create a collapsed line based on collapsed text properties.
- ///
- /// A list of
- /// objects that represent the collapsed text properties.
- ///
- /// A value that represents a collapsed line that can be displayed.
- ///
- public abstract TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList);
-
- ///
- /// Gets the character hit corresponding to the specified distance from the beginning of the line.
- ///
- /// A value that represents the distance from the beginning of the line.
- /// The object at the specified distance from the beginning of the line.
- public abstract CharacterHit GetCharacterHitFromDistance(double distance);
-
- ///
- /// Gets the distance from the beginning of the line to the specified character hit.
- /// .
- ///
- /// The object whose distance you want to query.
- /// A that represents the distance from the beginning of the line.
- public abstract double GetDistanceFromCharacterHit(CharacterHit characterHit);
-
- ///
- /// Gets the next character hit for caret navigation.
- ///
- /// The current .
- /// The next .
- public abstract CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit);
-
- ///
- /// Gets the previous character hit for caret navigation.
- ///
- /// The current .
- /// The previous .
- public abstract CharacterHit GetPreviousCaretCharacterHit(CharacterHit characterHit);
-
- ///
- /// Gets the previous character hit after backspacing.
- ///
- /// The current .
- /// The after backspacing.
- public abstract CharacterHit GetBackspaceCaretCharacterHit(CharacterHit characterHit);
-
- ///
- /// Gets the text line offset x.
- ///
- /// The line width.
- /// The paragraph width including whitespace.
- /// The paragraph width.
- /// The text alignment.
- /// The flow direction of the line.
- /// The paragraph offset.
- internal static double GetParagraphOffsetX(double width, double widthIncludingTrailingWhitespace,
- double paragraphWidth, TextAlignment textAlignment, FlowDirection flowDirection)
- {
- if (double.IsPositiveInfinity(paragraphWidth))
- {
- return 0;
- }
-
- if (flowDirection == FlowDirection.LeftToRight)
- {
- switch (textAlignment)
- {
- case TextAlignment.Center:
- return Math.Max(0, (paragraphWidth - width) / 2);
-
- case TextAlignment.Right:
- return Math.Max(0, paragraphWidth - widthIncludingTrailingWhitespace);
-
- default:
- return 0;
- }
- }
-
- switch (textAlignment)
- {
- case TextAlignment.Center:
- return Math.Max(0, (paragraphWidth - width) / 2);
-
- case TextAlignment.Right:
- return 0;
-
- default:
- return Math.Max(0, paragraphWidth - widthIncludingTrailingWhitespace);
- }
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs
deleted file mode 100644
index be9661c2bf..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System.Collections.Generic;
-
-namespace Avalonia.Media.TextFormatting
-{
- public class TextLineBreak
- {
- public TextLineBreak(TextEndOfLine? textEndOfLine = null, FlowDirection flowDirection = FlowDirection.LeftToRight,
- IReadOnlyList? remainingCharacters = null)
- {
- TextEndOfLine = textEndOfLine;
- FlowDirection = flowDirection;
- RemainingCharacters = remainingCharacters;
- }
-
- ///
- /// Get the end of line run.
- ///
- public TextEndOfLine? TextEndOfLine { get; }
-
- ///
- /// Get the flow direction for remaining characters.
- ///
- public FlowDirection FlowDirection { get; }
-
- ///
- /// Get the remaining shaped characters that were split up by the during the formatting process.
- ///
- public IReadOnlyList? RemainingCharacters { get; }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
deleted file mode 100644
index 49bee6e776..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
+++ /dev/null
@@ -1,778 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Media.TextFormatting.Unicode;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting
-{
- internal class TextLineImpl : TextLine
- {
- private static readonly Comparer s_compareStart = Comparer.Default;
-
- private static readonly Comparison s_compareLogicalOrder =
- (a, b) => s_compareStart.Compare(a.Text.Start, b.Text.Start);
-
- private readonly List _textRuns;
- private readonly double _paragraphWidth;
- private readonly TextParagraphProperties _paragraphProperties;
- private TextLineMetrics _textLineMetrics;
- private readonly FlowDirection _flowDirection;
-
- public TextLineImpl(List textRuns, TextRange textRange, double paragraphWidth,
- TextParagraphProperties paragraphProperties, FlowDirection flowDirection = FlowDirection.LeftToRight,
- TextLineBreak? lineBreak = null, bool hasCollapsed = false)
- {
- TextRange = textRange;
- TextLineBreak = lineBreak;
- HasCollapsed = hasCollapsed;
-
- _textRuns = textRuns;
- _paragraphWidth = paragraphWidth;
- _paragraphProperties = paragraphProperties;
-
- _flowDirection = flowDirection;
- }
-
- ///
- public override IReadOnlyList TextRuns => _textRuns;
-
- ///
- public override TextRange TextRange { get; }
-
- ///
- public override TextLineBreak? TextLineBreak { get; }
-
- ///
- public override bool HasCollapsed { get; }
-
- ///
- public override bool HasOverflowed => _textLineMetrics.HasOverflowed;
-
- ///
- public override double Baseline => _textLineMetrics.TextBaseline;
-
- ///
- public override double Extent => _textLineMetrics.Height;
-
- ///
- public override double Height => _textLineMetrics.Height;
-
- ///
- public override int NewLineLength => _textLineMetrics.NewLineLength;
-
- ///
- public override double OverhangAfter => 0;
-
- ///
- public override double OverhangLeading => 0;
-
- ///
- public override double OverhangTrailing => 0;
-
- ///
- public override int TrailingWhitespaceLength => _textLineMetrics.TrailingWhitespaceLength;
-
- ///
- public override double Start => _textLineMetrics.Start;
-
- ///
- public override double Width => _textLineMetrics.Width;
-
- ///
- public override double WidthIncludingTrailingWhitespace => _textLineMetrics.WidthIncludingTrailingWhitespace;
-
- ///
- public override void Draw(DrawingContext drawingContext, Point lineOrigin)
- {
- var (currentX, currentY) = lineOrigin;
-
- foreach (var textRun in _textRuns)
- {
- var offsetY = Baseline - textRun.GlyphRun.BaselineOrigin.Y;
-
- textRun.Draw(drawingContext, new Point(currentX, currentY + offsetY));
-
- currentX += textRun.Size.Width;
- }
- }
-
- ///
- public override TextLine Collapse(params TextCollapsingProperties[] collapsingPropertiesList)
- {
- if (collapsingPropertiesList.Length == 0)
- {
- return this;
- }
-
- var collapsingProperties = collapsingPropertiesList[0];
-
- var collapsedRuns = collapsingProperties.Collapse(this);
-
- if (collapsedRuns is List shapedRuns)
- {
- var collapsedLine = new TextLineImpl(shapedRuns, TextRange, _paragraphWidth, _paragraphProperties, _flowDirection, TextLineBreak, true);
-
- if (shapedRuns.Count > 0)
- {
- collapsedLine.FinalizeLine();
- }
-
- return collapsedLine;
- }
-
- return this;
- }
-
- ///
- public override CharacterHit GetCharacterHitFromDistance(double distance)
- {
- distance -= Start;
-
- if (distance <= 0)
- {
- // hit happens before the line, return the first position
- var firstRun = _textRuns[0];
-
- return firstRun.GlyphRun.GetCharacterHitFromDistance(distance, out _);
- }
-
- // process hit that happens within the line
- var characterHit = new CharacterHit();
-
- foreach (var run in _textRuns)
- {
- characterHit = run.GlyphRun.GetCharacterHitFromDistance(distance, out _);
-
- if (distance <= run.Size.Width)
- {
- break;
- }
-
- distance -= run.Size.Width;
- }
-
- return characterHit;
- }
-
- ///
- public override double GetDistanceFromCharacterHit(CharacterHit characterHit)
- {
- var characterIndex = characterHit.FirstCharacterIndex + (characterHit.TrailingLength != 0 ? 1 : 0);
-
- var currentDistance = Start;
-
- GlyphRun? lastRun = null;
-
- for (var index = 0; index < _textRuns.Count; index++)
- {
- var textRun = _textRuns[index];
- var currentRun = textRun.GlyphRun;
-
- if (lastRun != null)
- {
- if (!lastRun.IsLeftToRight && currentRun.IsLeftToRight &&
- currentRun.Characters.Start == characterHit.FirstCharacterIndex &&
- characterHit.TrailingLength == 0)
- {
- return currentDistance;
- }
- }
-
- //Look for a hit in within the current run
- if (characterIndex >= textRun.Text.Start && characterIndex <= textRun.Text.End)
- {
- var distance = currentRun.GetDistanceFromCharacterHit(characterHit);
-
- return currentDistance + distance;
- }
-
- //Look at the left and right edge of the current run
- if (currentRun.IsLeftToRight)
- {
- if (lastRun == null || lastRun.IsLeftToRight)
- {
- if (characterIndex <= textRun.Text.Start)
- {
- return currentDistance;
- }
- }
- else
- {
- if (characterIndex == textRun.Text.Start)
- {
- return currentDistance;
- }
- }
-
- if (characterIndex == textRun.Text.Start + textRun.Text.Length && characterHit.TrailingLength > 0)
- {
- return currentDistance + currentRun.Size.Width;
- }
- }
- else
- {
- if (characterIndex == textRun.Text.Start)
- {
- return currentDistance + currentRun.Size.Width;
- }
-
- var nextRun = index + 1 < _textRuns.Count ? _textRuns[index + 1] : null;
-
- if (nextRun != null)
- {
- if (characterHit.FirstCharacterIndex == textRun.Text.End && nextRun.ShapedBuffer.IsLeftToRight)
- {
- return currentDistance;
- }
-
- if (characterIndex > textRun.Text.End && nextRun.Text.End < textRun.Text.End)
- {
- return currentDistance;
- }
- }
- else
- {
- if (characterIndex > textRun.Text.End)
- {
- return currentDistance;
- }
- }
- }
-
- //No hit hit found so we add the full width
- currentDistance += currentRun.Size.Width;
-
- lastRun = currentRun;
- }
-
- return currentDistance;
- }
-
- ///
- public override CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit)
- {
- if (TryFindNextCharacterHit(characterHit, out var nextCharacterHit))
- {
- return nextCharacterHit;
- }
-
- // Can't move, we're after the last character
- var runIndex = GetRunIndexAtCharacterIndex(TextRange.End, LogicalDirection.Forward);
-
- var textRun = _textRuns[runIndex];
-
- characterHit = textRun.GlyphRun.GetNextCaretCharacterHit(characterHit);
-
- return characterHit;
- }
-
- ///
- public override CharacterHit GetPreviousCaretCharacterHit(CharacterHit characterHit)
- {
- if (TryFindPreviousCharacterHit(characterHit, out var previousCharacterHit))
- {
- return previousCharacterHit;
- }
-
- if (characterHit.FirstCharacterIndex <= TextRange.Start)
- {
- characterHit = new CharacterHit(TextRange.Start);
- }
-
- return characterHit; // Can't move, we're before the first character
- }
-
- ///
- public override CharacterHit GetBackspaceCaretCharacterHit(CharacterHit characterHit)
- {
- // same operation as move-to-previous
- return GetPreviousCaretCharacterHit(characterHit);
- }
-
- public static void SortRuns(List textRuns)
- {
- textRuns.Sort(s_compareLogicalOrder);
- }
-
- public TextLineImpl FinalizeLine()
- {
- BidiReorder();
-
- _textLineMetrics = CreateLineMetrics();
-
- return this;
- }
-
- private void BidiReorder()
- {
- // Build up the collection of ordered runs.
- var run = _textRuns[0];
- OrderedBidiRun orderedRun = new(run);
- var current = orderedRun;
-
- for (var i = 1; i < _textRuns.Count; i++)
- {
- run = _textRuns[i];
-
- current.Next = new OrderedBidiRun(run);
-
- current = current.Next;
- }
-
- // Reorder them into visual order.
- orderedRun = LinearReOrder(orderedRun);
-
- // Now perform a recursive reversal of each run.
- // From the highest level found in the text to the lowest odd level on each line, including intermediate levels
- // not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
- // https://unicode.org/reports/tr9/#L2
- sbyte max = 0;
- var min = sbyte.MaxValue;
-
- for (var i = 0; i < _textRuns.Count; i++)
- {
- var level = _textRuns[i].BidiLevel;
-
- if (level > max)
- {
- max = level;
- }
-
- if ((level & 1) != 0 && level < min)
- {
- min = level;
- }
- }
-
- if (min > max)
- {
- min = max;
- }
-
- if (max == 0 || (min == max && (max & 1) == 0))
- {
- // Nothing to reverse.
- return;
- }
-
- // Now apply the reversal and replace the original contents.
- var minLevelToReverse = max;
-
- while (minLevelToReverse >= min)
- {
- current = orderedRun;
-
- while (current != null)
- {
- if (current.Level >= minLevelToReverse && current.Level % 2 != 0)
- {
- if (!current.Run.IsReversed)
- {
- current.Run.Reverse();
- }
- }
-
- current = current.Next;
- }
-
- minLevelToReverse--;
- }
-
- _textRuns.Clear();
-
- current = orderedRun;
-
- while (current != null)
- {
- _textRuns.Add(current.Run);
-
- current = current.Next;
- }
- }
-
- ///
- /// Reorders a series of runs from logical to visual order, returning the left most run.
- ///
- ///
- /// The ordered bidi run.
- /// The .
- private static OrderedBidiRun LinearReOrder(OrderedBidiRun? run)
- {
- BidiRange? range = null;
-
- while (run != null)
- {
- var next = run.Next;
-
- while (range != null && range.Level > run.Level
- && range.Previous != null && range.Previous.Level >= run.Level)
- {
- range = BidiRange.MergeWithPrevious(range);
- }
-
- if (range != null && range.Level >= run.Level)
- {
- // Attach run to the range.
- if ((run.Level & 1) != 0)
- {
- // Odd, range goes to the right of run.
- run.Next = range.Left;
- range.Left = run;
- }
- else
- {
- // Even, range goes to the left of run.
- range.Right!.Next = run;
- range.Right = run;
- }
-
- range.Level = run.Level;
- }
- else
- {
- var r = new BidiRange();
-
- r.Left = r.Right = run;
- r.Level = run.Level;
- r.Previous = range;
-
- range = r;
- }
-
- run = next;
- }
-
- while (range?.Previous != null)
- {
- range = BidiRange.MergeWithPrevious(range);
- }
-
- // Terminate.
- range!.Right!.Next = null;
-
- return range.Left!;
- }
-
- ///
- /// Tries to find the next character hit.
- ///
- /// The current character hit.
- /// The next character hit.
- ///
- private bool TryFindNextCharacterHit(CharacterHit characterHit, out CharacterHit nextCharacterHit)
- {
- nextCharacterHit = characterHit;
-
- var codepointIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
-
- if (codepointIndex >= TextRange.End)
- {
- return false; // Cannot go forward anymore
- }
-
- if (codepointIndex < TextRange.Start)
- {
- codepointIndex = TextRange.Start;
- }
-
- var runIndex = GetRunIndexAtCharacterIndex(codepointIndex, LogicalDirection.Forward);
-
- while (runIndex < _textRuns.Count)
- {
- var run = _textRuns[runIndex];
-
- var foundCharacterHit =
- run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength,
- out _);
-
- var isAtEnd = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength ==
- TextRange.Start + TextRange.Length;
-
- if (isAtEnd && !run.GlyphRun.IsLeftToRight)
- {
- nextCharacterHit = foundCharacterHit;
-
- return true;
- }
-
- var characterIndex = codepointIndex - run.Text.Start;
-
- if (characterIndex < 0 && run.ShapedBuffer.IsLeftToRight)
- {
- foundCharacterHit = new CharacterHit(foundCharacterHit.FirstCharacterIndex);
- }
-
- nextCharacterHit = isAtEnd || characterHit.TrailingLength != 0 ?
- foundCharacterHit :
- new CharacterHit(foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength);
-
- if (isAtEnd || nextCharacterHit.FirstCharacterIndex > characterHit.FirstCharacterIndex)
- {
- return true;
- }
-
- runIndex++;
- }
-
- return false;
- }
-
- ///
- /// Tries to find the previous character hit.
- ///
- /// The current character hit.
- /// The previous character hit.
- ///
- private bool TryFindPreviousCharacterHit(CharacterHit characterHit, out CharacterHit previousCharacterHit)
- {
- var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
-
- if (characterIndex == TextRange.Start)
- {
- previousCharacterHit = new CharacterHit(TextRange.Start);
-
- return true;
- }
-
- previousCharacterHit = characterHit;
-
- if (characterIndex < TextRange.Start)
- {
- return false; // Cannot go backward anymore.
- }
-
- var runIndex = GetRunIndexAtCharacterIndex(characterIndex, LogicalDirection.Backward);
-
- while (runIndex >= 0)
- {
- var run = _textRuns[runIndex];
-
- var foundCharacterHit =
- run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _);
-
- if (foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength < characterIndex)
- {
- previousCharacterHit = foundCharacterHit;
-
- return true;
- }
-
- previousCharacterHit = characterHit.TrailingLength != 0 ?
- foundCharacterHit :
- new CharacterHit(foundCharacterHit.FirstCharacterIndex);
-
- if (previousCharacterHit != characterHit)
- {
- return true;
- }
-
- runIndex--;
- }
-
- return false;
- }
-
- ///
- /// Gets the run index of the specified codepoint index.
- ///
- /// The codepoint index.
- /// The logical direction.
- /// The text run index.
- private int GetRunIndexAtCharacterIndex(int codepointIndex, LogicalDirection direction)
- {
- var runIndex = 0;
- ShapedTextCharacters? previousRun = null;
-
- while (runIndex < _textRuns.Count)
- {
- var currentRun = _textRuns[runIndex];
-
- if (previousRun != null && !previousRun.ShapedBuffer.IsLeftToRight)
- {
- if (currentRun.ShapedBuffer.IsLeftToRight)
- {
- if (currentRun.Text.Start >= codepointIndex)
- {
- return --runIndex;
- }
- }
- else
- {
- if (codepointIndex > currentRun.Text.Start + currentRun.Text.Length)
- {
- return --runIndex;
- }
- }
- }
-
- if (direction == LogicalDirection.Forward)
- {
- if (codepointIndex >= currentRun.Text.Start && codepointIndex <= currentRun.Text.End)
- {
- return runIndex;
- }
- }
- else
- {
- if (codepointIndex > currentRun.Text.Start &&
- codepointIndex <= currentRun.Text.Start + currentRun.Text.Length)
- {
- return runIndex;
- }
- }
-
- if (runIndex + 1 < _textRuns.Count)
- {
- runIndex++;
- previousRun = currentRun;
- }
- else
- {
- break;
- }
- }
-
- return runIndex;
- }
-
- private TextLineMetrics CreateLineMetrics()
- {
- var width = 0d;
- var widthIncludingWhitespace = 0d;
- var trailingWhitespaceLength = 0;
- var newLineLength = 0;
- var ascent = 0d;
- var descent = 0d;
- var lineGap = 0d;
- var fontRenderingEmSize = 0d;
-
- for (var index = 0; index < _textRuns.Count; index++)
- {
- var textRun = _textRuns[index];
-
- var fontMetrics =
- new FontMetrics(textRun.Properties.Typeface, textRun.Properties.FontRenderingEmSize);
-
- if (fontRenderingEmSize < textRun.Properties.FontRenderingEmSize)
- {
- fontRenderingEmSize = textRun.Properties.FontRenderingEmSize;
-
- if (ascent > fontMetrics.Ascent)
- {
- ascent = fontMetrics.Ascent;
- }
-
- if (descent < fontMetrics.Descent)
- {
- descent = fontMetrics.Descent;
- }
-
- if (lineGap < fontMetrics.LineGap)
- {
- lineGap = fontMetrics.LineGap;
- }
- }
-
- switch (_paragraphProperties.FlowDirection)
- {
- case FlowDirection.LeftToRight:
- {
- if (index == _textRuns.Count - 1)
- {
- width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width;
- trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength;
- newLineLength = textRun.GlyphRun.Metrics.NewlineLength;
- }
-
- break;
- }
-
- case FlowDirection.RightToLeft:
- {
- if (index == _textRuns.Count - 1)
- {
- var firstRun = _textRuns[0];
-
- var offset = firstRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace -
- firstRun.GlyphRun.Metrics.Width;
-
- width = widthIncludingWhitespace +
- textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - offset;
-
- trailingWhitespaceLength = firstRun.GlyphRun.Metrics.TrailingWhitespaceLength;
- newLineLength = firstRun.GlyphRun.Metrics.NewlineLength;
- }
-
- break;
- }
- }
-
- widthIncludingWhitespace += textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace;
- }
-
- var start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth,
- _paragraphProperties.TextAlignment, _paragraphProperties.FlowDirection);
-
- var lineHeight = _paragraphProperties.LineHeight;
-
- var height = double.IsNaN(lineHeight) || MathUtilities.IsZero(lineHeight) ?
- descent - ascent + lineGap :
- lineHeight;
-
- return new TextLineMetrics(widthIncludingWhitespace > _paragraphWidth, height, newLineLength, start,
- -ascent, trailingWhitespaceLength, width, widthIncludingWhitespace);
- }
-
- private sealed class OrderedBidiRun
- {
- public OrderedBidiRun(ShapedTextCharacters run) => Run = run;
-
- public sbyte Level => Run.BidiLevel;
-
- public ShapedTextCharacters Run { get; }
-
- public OrderedBidiRun? Next { get; set; }
-
- public void Reverse() => Run.ShapedBuffer.GlyphInfos.Span.Reverse();
- }
-
- private sealed class BidiRange
- {
- public int Level { get; set; }
-
- public OrderedBidiRun? Left { get; set; }
-
- public OrderedBidiRun? Right { get; set; }
-
- public BidiRange? Previous { get; set; }
-
- public static BidiRange MergeWithPrevious(BidiRange range)
- {
- var previous = range.Previous;
-
- BidiRange left;
- BidiRange right;
-
- if ((previous!.Level & 1) != 0)
- {
- // Odd, previous goes to the right of range.
- left = range;
- right = previous;
- }
- else
- {
- // Even, previous goes to the left of range.
- left = previous;
- right = range;
- }
-
- // Stitch them
- left.Right!.Next = right.Left;
- previous.Left = left.Left;
- previous.Right = right.Right;
-
- return previous;
- }
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs
deleted file mode 100644
index b799567a60..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// Provides a set of properties that are used during the paragraph layout.
- ///
- public abstract class TextParagraphProperties
- {
- ///
- /// This property specifies whether the primary text advance
- /// direction shall be left-to-right, right-to-left.
- ///
- public abstract FlowDirection FlowDirection { get; }
-
- ///
- /// Gets the text alignment.
- ///
- public abstract TextAlignment TextAlignment { get; }
-
- ///
- /// Paragraph's line height
- ///
- public abstract double LineHeight { get; }
-
- ///
- /// Indicates the first line of the paragraph.
- ///
- public abstract bool FirstLineInParagraph { get; }
-
- ///
- /// If true, the formatted line may always be collapsed. If false (the default),
- /// only lines that overflow the paragraph width are collapsed.
- ///
- public virtual bool AlwaysCollapsible
- {
- get { return false; }
- }
-
- ///
- /// Gets the default text style.
- ///
- public abstract TextRunProperties DefaultTextRunProperties { get; }
-
- ///
- /// If not null, text decorations to apply to all runs in the line. This is in addition
- /// to any text decorations specified by the TextRunProperties for individual text runs.
- ///
- public virtual TextDecorationCollection? TextDecorations => null;
-
- ///
- /// Gets the text wrapping.
- ///
- public abstract TextWrapping TextWrapping { get; }
-
- ///
- /// Line indentation
- ///
- public abstract double Indent { get; }
-
- ///
- /// Paragraph indentation
- ///
- public virtual double ParagraphIndent
- {
- get { return 0; }
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs
deleted file mode 100644
index 99fcbd805f..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-using System;
-using System.Globalization;
-
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// Provides a set of properties, such as typeface or foreground brush, that can be applied to a TextRun object. This is an abstract class.
- ///
- ///
- /// The text layout client provides a concrete implementation of this abstract class.
- /// This enables the client to implement text run properties in a way that corresponds with the associated formatting store.
- ///
- public abstract class TextRunProperties : IEquatable
- {
- ///
- /// Run typeface
- ///
- public abstract Typeface Typeface { get; }
-
- ///
- /// Em size of font used to format and display text
- ///
- public abstract double FontRenderingEmSize { get; }
-
- ///
- /// Run TextDecorations.
- ///
- public abstract TextDecorationCollection? TextDecorations { get; }
-
- ///
- /// Brush used to fill text.
- ///
- public abstract IBrush? ForegroundBrush { get; }
-
- ///
- /// Brush used to paint background of run.
- ///
- public abstract IBrush? BackgroundBrush { get; }
-
- ///
- /// Run text culture.
- ///
- public abstract CultureInfo? CultureInfo { get; }
-
- ///
- /// Run vertical box alignment
- ///
- public abstract BaselineAlignment BaselineAlignment { get; }
-
- public bool Equals(TextRunProperties? other)
- {
- if (ReferenceEquals(null, other))
- return false;
- if (ReferenceEquals(this, other))
- return true;
-
- return Typeface.Equals(other.Typeface) &&
- FontRenderingEmSize.Equals(other.FontRenderingEmSize)
- && Equals(TextDecorations, other.TextDecorations) &&
- Equals(ForegroundBrush, other.ForegroundBrush) &&
- Equals(BackgroundBrush, other.BackgroundBrush) &&
- Equals(CultureInfo, other.CultureInfo);
- }
-
- public override bool Equals(object? obj)
- {
- return ReferenceEquals(this, obj) || obj is TextRunProperties other && Equals(other);
- }
-
- public override int GetHashCode()
- {
- unchecked
- {
- var hashCode = Typeface.GetHashCode();
- hashCode = (hashCode * 397) ^ FontRenderingEmSize.GetHashCode();
- hashCode = (hashCode * 397) ^ (TextDecorations != null ? TextDecorations.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (ForegroundBrush != null ? ForegroundBrush.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (BackgroundBrush != null ? BackgroundBrush.GetHashCode() : 0);
- hashCode = (hashCode * 397) ^ (CultureInfo != null ? CultureInfo.GetHashCode() : 0);
- return hashCode;
- }
- }
-
- public static bool operator ==(TextRunProperties left, TextRunProperties right)
- {
- return Equals(left, right);
- }
-
- public static bool operator !=(TextRunProperties left, TextRunProperties right)
- {
- return !Equals(left, right);
- }
-
- internal TextRunProperties WithTypeface(Typeface typeface)
- {
- return new GenericTextRunProperties(typeface, FontRenderingEmSize,
- TextDecorations, ForegroundBrush, BackgroundBrush, BaselineAlignment);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs
deleted file mode 100644
index c982a435c3..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System;
-using System.Globalization;
-using Avalonia.Platform;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// A class that is responsible for text shaping.
- ///
- public class TextShaper
- {
- private readonly ITextShaperImpl _platformImpl;
-
- public TextShaper(ITextShaperImpl platformImpl)
- {
- _platformImpl = platformImpl;
- }
-
- ///
- /// Gets the current text shaper.
- ///
- public static TextShaper Current
- {
- get
- {
- var current = AvaloniaLocator.Current.GetService();
-
- if (current != null)
- {
- return current;
- }
-
- var textShaperImpl = AvaloniaLocator.Current.GetService();
-
- if (textShaperImpl == null)
- throw new InvalidOperationException("No text shaper implementation was registered.");
-
- current = new TextShaper(textShaperImpl);
-
- AvaloniaLocator.CurrentMutable.Bind().ToConstant(current);
-
- return current;
- }
- }
-
- ///
- public ShapedBuffer ShapeText(ReadOnlySlice text, GlyphTypeface typeface, double fontRenderingEmSize,
- CultureInfo? culture, sbyte bidiLevel)
- {
- return _platformImpl.ShapeText(text, typeface, fontRenderingEmSize, culture, bidiLevel);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
deleted file mode 100644
index 83acaa021e..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System.Collections.Generic;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// A collapsing properties to collapse whole line toward the end
- /// at character granularity.
- ///
- public sealed class TextTrailingCharacterEllipsis : TextCollapsingProperties
- {
- ///
- /// Construct a text trailing character ellipsis collapsing properties
- ///
- /// Text used as collapsing symbol.
- /// Width in which collapsing is constrained to.
- /// Text run properties of ellispis symbol.
- public TextTrailingCharacterEllipsis(ReadOnlySlice ellipsis, double width, TextRunProperties textRunProperties)
- {
- Width = width;
- Symbol = new TextCharacters(ellipsis, textRunProperties);
- }
-
- ///
- public sealed override double Width { get; }
-
- ///
- public sealed override TextRun Symbol { get; }
-
- public override IReadOnlyList? Collapse(TextLine textLine)
- {
- return TextEllipsisHelper.Collapse(textLine, this, false);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs
deleted file mode 100644
index ff2e4cf325..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.Collections.Generic;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting
-{
- ///
- /// a collapsing properties to collapse whole line toward the end
- /// at word granularity.
- ///
- public sealed class TextTrailingWordEllipsis : TextCollapsingProperties
- {
- ///
- /// Construct a text trailing word ellipsis collapsing properties.
- ///
- /// Text used as collapsing symbol.
- /// width in which collapsing is constrained to.
- /// text run properties of ellispis symbol.
- public TextTrailingWordEllipsis(
- ReadOnlySlice ellipsis,
- double width,
- TextRunProperties textRunProperties
- )
- {
- Width = width;
- Symbol = new TextCharacters(ellipsis, textRunProperties);
- }
-
- ///
- public sealed override double Width { get; }
-
- ///
- public sealed override TextRun Symbol { get; }
-
- public override IReadOnlyList? Collapse(TextLine textLine)
- {
- return TextEllipsisHelper.Collapse(textLine, this, true);
- }
- }
-}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiAlgorithm.cs b/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiAlgorithm.cs
deleted file mode 100644
index 404956d1e1..0000000000
--- a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiAlgorithm.cs
+++ /dev/null
@@ -1,1717 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-// Ported from: https://github.com/SixLabors/Fonts/
-
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using Avalonia.Utilities;
-
-namespace Avalonia.Media.TextFormatting.Unicode
-{
- ///
- /// Implementation of Unicode bidirectional algorithm (UAX #9)
- /// https://unicode.org/reports/tr9/
- ///
- ///
- ///
- /// The Bidi algorithm uses a number of memory arrays for resolved
- /// types, level information, bracket types, x9 removal maps and
- /// more...
- ///
- ///
- /// This implementation of the BiDi algorithm has been designed
- /// to reduce memory pressure on the GC by re-using the same
- /// work buffers, so instances of this class should be re-used
- /// as much as possible.
- ///
- ///
- internal sealed class BidiAlgorithm
- {
- ///
- /// The original BiDiClass classes as provided by the caller
- ///
- private ArraySlice _originalClasses;
-
- ///
- /// Paired bracket types as provided by caller
- ///
- private ArraySlice _pairedBracketTypes;
-
- ///
- /// Paired bracket values as provided by caller
- ///
- private ArraySlice _pairedBracketValues;
-
- ///
- /// Try if the incoming data is known to contain brackets
- ///
- private bool _hasBrackets;
-
- ///
- /// True if the incoming data is known to contain embedding runs
- ///
- private bool _hasEmbeddings;
-
- ///
- /// True if the incoming data is known to contain isolating runs
- ///
- private bool _hasIsolates;
-
- ///
- /// Two directional mapping of isolate start/end pairs
- ///
- ///
- /// The forward mapping maps the start index to the end index.
- /// The reverse mapping maps the end index to the start index.
- ///
- private readonly Dictionary _isolatePairs = new Dictionary();
-
- ///
- /// The working BiDi classes
- ///
- private ArraySlice _workingClasses;
-
- ///
- /// The working classes buffer
- ///
- private ArrayBuilder _workingClassesBuffer;
-
- ///
- /// A slice of the resolved levels
- ///
- private ArraySlice _resolvedLevels;
-
- ///
- /// The buffer underlying resolvedLevels
- ///
- private ArrayBuilder _resolvedLevelsBuffer;
-
- ///
- /// The resolve paragraph embedding level
- ///
- private sbyte _paragraphEmbeddingLevel;
-
- ///
- /// The status stack used during resolution of explicit
- /// embedding and isolating runs
- ///
- private readonly Stack _statusStack = new Stack();
-
- ///
- /// Mapping used to virtually remove characters for rule X9
- ///
- private ArrayBuilder _x9Map;
-
- ///
- /// Re-usable list of level runs
- ///
- private readonly List _levelRuns = new List();
-
- ///
- /// Mapping for the current isolating sequence, built
- /// by joining level runs from the x9 map.
- ///
- private ArrayBuilder _isolatedRunMapping;
-
- ///
- /// A stack of pending isolate openings used by FindIsolatePairs()
- ///
- private readonly Stack _pendingIsolateOpenings = new Stack();
-
- ///
- /// The level of the isolating run currently being processed
- ///
- private int _runLevel;
-
- ///
- /// The direction of the isolating run currently being processed
- ///
- private BidiClass _runDirection;
-
- ///
- /// The length of the isolating run currently being processed
- ///
- private int _runLength;
-
- ///
- /// A mapped slice of the resolved types for the isolating run currently
- /// being processed
- ///
- private MappedArraySlice _runResolvedClasses;
-
- ///
- /// A mapped slice of the original types for the isolating run currently
- /// being processed
- ///
- private MappedArraySlice _runOriginalClasses;
-
- ///
- /// A mapped slice of the run levels for the isolating run currently
- /// being processed
- ///
- private MappedArraySlice _runLevels;
-
- ///
- /// A mapped slice of the paired bracket types of the isolating
- /// run currently being processed
- ///
- private MappedArraySlice _runBiDiPairedBracketTypes;
-
- ///
- /// A mapped slice of the paired bracket values of the isolating
- /// run currently being processed
- ///
- private MappedArraySlice _runPairedBracketValues;
-
- ///
- /// Maximum pairing depth for paired brackets
- ///
- private const int MaxPairedBracketDepth = 63;
-
- ///
- /// Reusable list of pending opening brackets used by the
- /// LocatePairedBrackets method
- ///
- private readonly List _pendingOpeningBrackets = new List